mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Merge remote-tracking branch 'upstream/master' into string-interpolation
This commit is contained in:
commit
cce6b98821
139 changed files with 5147 additions and 2214 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -8,3 +8,6 @@
|
|||
/default.prof*
|
||||
/fuzz-*
|
||||
/luau
|
||||
/luau-tests
|
||||
/luau-analyze
|
||||
__pycache__
|
||||
|
|
32
Analysis/include/Luau/ApplyTypeFunction.h
Normal file
32
Analysis/include/Luau/ApplyTypeFunction.h
Normal 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
|
|
@ -4,6 +4,7 @@
|
|||
#include "Luau/Ast.h" // Used for some of the enumerations
|
||||
#include "Luau/NotNull.h"
|
||||
#include "Luau/Variant.h"
|
||||
#include "Luau/TypeVar.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
@ -12,7 +13,8 @@
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
struct Scope2;
|
||||
struct Scope;
|
||||
|
||||
struct TypeVar;
|
||||
using TypeId = const TypeVar*;
|
||||
|
||||
|
@ -38,7 +40,7 @@ struct GeneralizationConstraint
|
|||
{
|
||||
TypeId generalizedType;
|
||||
TypeId sourceType;
|
||||
Scope2* scope;
|
||||
Scope* scope;
|
||||
};
|
||||
|
||||
// subType ~ inst superType
|
||||
|
@ -70,8 +72,15 @@ struct NameConstraint
|
|||
std::string name;
|
||||
};
|
||||
|
||||
// target ~ inst target
|
||||
struct TypeAliasExpansionConstraint
|
||||
{
|
||||
// Must be a PendingExpansionTypeVar.
|
||||
TypeId target;
|
||||
};
|
||||
|
||||
using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, UnaryConstraint,
|
||||
BinaryConstraint, NameConstraint>;
|
||||
BinaryConstraint, NameConstraint, TypeAliasExpansionConstraint>;
|
||||
using ConstraintPtr = std::unique_ptr<struct Constraint>;
|
||||
|
||||
struct Constraint
|
||||
|
|
|
@ -17,21 +17,22 @@
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
struct Scope2;
|
||||
struct Scope;
|
||||
using ScopePtr = std::shared_ptr<Scope>;
|
||||
|
||||
struct ConstraintGraphBuilder
|
||||
{
|
||||
// A list of all the scopes in the module. This vector holds ownership of the
|
||||
// scope pointers; the scopes themselves borrow pointers to other scopes to
|
||||
// define the scope hierarchy.
|
||||
std::vector<std::pair<Location, std::unique_ptr<Scope2>>> scopes;
|
||||
std::vector<std::pair<Location, ScopePtr>> scopes;
|
||||
|
||||
ModuleName moduleName;
|
||||
SingletonTypes& singletonTypes;
|
||||
const NotNull<TypeArena> arena;
|
||||
// The root scope of the module we're generating constraints for.
|
||||
// This is null when the CGB is initially constructed.
|
||||
Scope2* rootScope;
|
||||
Scope* rootScope;
|
||||
// A mapping of AST node to TypeId.
|
||||
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
|
||||
// A mapping of AST node to TypePackId.
|
||||
|
@ -41,6 +42,8 @@ struct ConstraintGraphBuilder
|
|||
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
|
||||
// Type packs resolved from type annotations. Analogous to astTypePacks.
|
||||
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
|
||||
// Defining scopes for AST nodes.
|
||||
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};
|
||||
|
||||
int recursionCount = 0;
|
||||
|
||||
|
@ -50,42 +53,42 @@ struct ConstraintGraphBuilder
|
|||
// Occasionally constraint generation needs to produce an ICE.
|
||||
const NotNull<InternalErrorReporter> ice;
|
||||
|
||||
NotNull<Scope2> globalScope;
|
||||
NotNull<Scope> globalScope;
|
||||
|
||||
ConstraintGraphBuilder(const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope2> globalScope);
|
||||
ConstraintGraphBuilder(const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope> globalScope);
|
||||
|
||||
/**
|
||||
* Fabricates a new free type belonging to a given scope.
|
||||
* @param scope the scope the free type belongs to.
|
||||
*/
|
||||
TypeId freshType(NotNull<Scope2> scope);
|
||||
TypeId freshType(const ScopePtr& scope);
|
||||
|
||||
/**
|
||||
* Fabricates a new free type pack belonging to a given scope.
|
||||
* @param scope the scope the free type pack belongs to.
|
||||
*/
|
||||
TypePackId freshTypePack(NotNull<Scope2> scope);
|
||||
TypePackId freshTypePack(const ScopePtr& scope);
|
||||
|
||||
/**
|
||||
* Fabricates a scope that is a child of another scope.
|
||||
* @param location the lexical extent of the scope in the source code.
|
||||
* @param parent the parent scope of the new scope. Must not be null.
|
||||
*/
|
||||
NotNull<Scope2> childScope(Location location, NotNull<Scope2> parent);
|
||||
ScopePtr childScope(Location location, const ScopePtr& parent);
|
||||
|
||||
/**
|
||||
* Adds a new constraint with no dependencies to a given scope.
|
||||
* @param scope the scope to add the constraint to.
|
||||
* @param cv the constraint variant to add.
|
||||
*/
|
||||
void addConstraint(NotNull<Scope2> scope, ConstraintV cv);
|
||||
void addConstraint(const ScopePtr& scope, ConstraintV cv);
|
||||
|
||||
/**
|
||||
* Adds a constraint to a given scope.
|
||||
* @param scope the scope to add the constraint to. Must not be null.
|
||||
* @param c the constraint to add.
|
||||
*/
|
||||
void addConstraint(NotNull<Scope2> scope, std::unique_ptr<Constraint> c);
|
||||
void addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c);
|
||||
|
||||
/**
|
||||
* The entry point to the ConstraintGraphBuilder. This will construct a set
|
||||
|
@ -94,23 +97,26 @@ struct ConstraintGraphBuilder
|
|||
*/
|
||||
void visit(AstStatBlock* block);
|
||||
|
||||
void visitBlockWithoutChildScope(NotNull<Scope2> scope, AstStatBlock* block);
|
||||
void visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block);
|
||||
|
||||
void visit(NotNull<Scope2> scope, AstStat* stat);
|
||||
void visit(NotNull<Scope2> scope, AstStatBlock* block);
|
||||
void visit(NotNull<Scope2> scope, AstStatLocal* local);
|
||||
void visit(NotNull<Scope2> scope, AstStatFor* for_);
|
||||
void visit(NotNull<Scope2> scope, AstStatLocalFunction* function);
|
||||
void visit(NotNull<Scope2> scope, AstStatFunction* function);
|
||||
void visit(NotNull<Scope2> scope, AstStatReturn* ret);
|
||||
void visit(NotNull<Scope2> scope, AstStatAssign* assign);
|
||||
void visit(NotNull<Scope2> scope, AstStatIf* ifStatement);
|
||||
void visit(NotNull<Scope2> scope, AstStatTypeAlias* alias);
|
||||
void visit(const ScopePtr& scope, AstStat* stat);
|
||||
void visit(const ScopePtr& scope, AstStatBlock* block);
|
||||
void visit(const ScopePtr& scope, AstStatLocal* local);
|
||||
void visit(const ScopePtr& scope, AstStatFor* for_);
|
||||
void visit(const ScopePtr& scope, AstStatLocalFunction* function);
|
||||
void visit(const ScopePtr& scope, AstStatFunction* function);
|
||||
void visit(const ScopePtr& scope, AstStatReturn* ret);
|
||||
void visit(const ScopePtr& scope, AstStatAssign* assign);
|
||||
void visit(const ScopePtr& scope, AstStatIf* ifStatement);
|
||||
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(NotNull<Scope2> scope, const AstArray<AstExpr*>& exprs);
|
||||
TypePackId checkExprList(const ScopePtr& scope, const AstArray<AstExpr*>& exprs);
|
||||
|
||||
TypePackId checkPack(NotNull<Scope2> scope, AstArray<AstExpr*> exprs);
|
||||
TypePackId checkPack(NotNull<Scope2> scope, AstExpr* expr);
|
||||
TypePackId checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs);
|
||||
TypePackId checkPack(const ScopePtr& scope, AstExpr* expr);
|
||||
|
||||
/**
|
||||
* Checks an expression that is expected to evaluate to one type.
|
||||
|
@ -118,13 +124,13 @@ struct ConstraintGraphBuilder
|
|||
* @param expr the expression to check.
|
||||
* @return the type of the expression.
|
||||
*/
|
||||
TypeId check(NotNull<Scope2> scope, AstExpr* expr);
|
||||
TypeId check(const ScopePtr& scope, AstExpr* expr);
|
||||
|
||||
TypeId checkExprTable(NotNull<Scope2> scope, AstExprTable* expr);
|
||||
TypeId check(NotNull<Scope2> scope, AstExprIndexName* indexName);
|
||||
TypeId check(NotNull<Scope2> scope, AstExprIndexExpr* indexExpr);
|
||||
TypeId check(NotNull<Scope2> scope, AstExprUnary* unary);
|
||||
TypeId check(NotNull<Scope2> scope, AstExprBinary* binary);
|
||||
TypeId checkExprTable(const ScopePtr& scope, AstExprTable* expr);
|
||||
TypeId check(const ScopePtr& scope, AstExprIndexName* indexName);
|
||||
TypeId check(const ScopePtr& scope, AstExprIndexExpr* indexExpr);
|
||||
TypeId check(const ScopePtr& scope, AstExprUnary* unary);
|
||||
TypeId check(const ScopePtr& scope, AstExprBinary* binary);
|
||||
|
||||
struct FunctionSignature
|
||||
{
|
||||
|
@ -133,28 +139,29 @@ struct ConstraintGraphBuilder
|
|||
// The scope that encompasses the function's signature. May be nullptr
|
||||
// if there was no need for a signature scope (the function has no
|
||||
// generics).
|
||||
Scope2* signatureScope;
|
||||
ScopePtr signatureScope;
|
||||
// The scope that encompasses the function's body. Is a child scope of
|
||||
// signatureScope, if present.
|
||||
NotNull<Scope2> bodyScope;
|
||||
ScopePtr bodyScope;
|
||||
};
|
||||
|
||||
FunctionSignature checkFunctionSignature(NotNull<Scope2> parent, AstExprFunction* fn);
|
||||
FunctionSignature checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn);
|
||||
|
||||
/**
|
||||
* Checks the body of a function expression.
|
||||
* @param scope the interior scope of the body of the function.
|
||||
* @param fn the function expression to check.
|
||||
*/
|
||||
void checkFunctionBody(NotNull<Scope2> scope, AstExprFunction* fn);
|
||||
void checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn);
|
||||
|
||||
/**
|
||||
* Resolves a type from its AST annotation.
|
||||
* @param scope the scope that the type annotation appears within.
|
||||
* @param ty the AST annotation to resolve.
|
||||
* @param topLevel whether the annotation is a "top-level" annotation.
|
||||
* @return the type of the AST annotation.
|
||||
**/
|
||||
TypeId resolveType(NotNull<Scope2> scope, AstType* ty);
|
||||
TypeId resolveType(const ScopePtr& scope, AstType* ty, bool topLevel = false);
|
||||
|
||||
/**
|
||||
* Resolves a type pack from its AST annotation.
|
||||
|
@ -162,14 +169,14 @@ struct ConstraintGraphBuilder
|
|||
* @param tp the AST annotation to resolve.
|
||||
* @return the type pack of the AST annotation.
|
||||
**/
|
||||
TypePackId resolveTypePack(NotNull<Scope2> scope, AstTypePack* tp);
|
||||
TypePackId resolveTypePack(const ScopePtr& scope, AstTypePack* tp);
|
||||
|
||||
TypePackId resolveTypePack(NotNull<Scope2> scope, const AstTypeList& list);
|
||||
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypeList& list);
|
||||
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> createGenerics(NotNull<Scope2> scope, AstArray<AstGenericType> generics);
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(NotNull<Scope2> scope, AstArray<AstGenericTypePack> packs);
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> createGenerics(const ScopePtr& scope, AstArray<AstGenericType> generics);
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(const ScopePtr& scope, AstArray<AstGenericTypePack> packs);
|
||||
|
||||
TypeId flattenPack(NotNull<Scope2> scope, Location location, TypePackId tp);
|
||||
TypeId flattenPack(const ScopePtr& scope, Location location, TypePackId tp);
|
||||
|
||||
void reportError(Location location, TypeErrorData err);
|
||||
void reportCodeTooComplex(Location location);
|
||||
|
@ -180,7 +187,7 @@ struct ConstraintGraphBuilder
|
|||
* real" in a general way is going to be pretty hard, so we are choosing not to tackle that yet. For now, we do an
|
||||
* initial scan of the AST and note what globals are defined.
|
||||
*/
|
||||
void prepopulateGlobalScope(NotNull<Scope2> globalScope, AstStatBlock* program);
|
||||
void prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -193,6 +200,6 @@ struct ConstraintGraphBuilder
|
|||
* @return a list of pointers to constraints contained within the scope graph.
|
||||
* None of these pointers should be null.
|
||||
*/
|
||||
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope2> rootScope);
|
||||
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope> rootScope);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -17,15 +17,35 @@ namespace Luau
|
|||
// never dereference this pointer.
|
||||
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
|
||||
{
|
||||
TypeArena* arena;
|
||||
InternalErrorReporter iceReporter;
|
||||
// The entire set of constraints that the solver is trying to resolve. It
|
||||
// is important to not add elements to this vector, lest the underlying
|
||||
// storage that we retain pointers to be mutated underneath us.
|
||||
const std::vector<NotNull<Constraint>> constraints;
|
||||
NotNull<Scope2> rootScope;
|
||||
// The entire set of constraints that the solver is trying to resolve.
|
||||
std::vector<NotNull<Constraint>> constraints;
|
||||
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.
|
||||
// A constraint can be both blocked and unsolved, for instance.
|
||||
|
@ -37,10 +57,12 @@ struct ConstraintSolver
|
|||
std::unordered_map<NotNull<const Constraint>, size_t> blockedConstraints;
|
||||
// A mapping of type/pack pointers to the constraints they block.
|
||||
std::unordered_map<BlockedConstraintId, std::vector<NotNull<const Constraint>>> blocked;
|
||||
// Memoized instantiations of type aliases.
|
||||
DenseHashMap<InstantiationSignature, TypeId, HashInstantiationSignature> instantiatedAliases{{}};
|
||||
|
||||
ConstraintSolverLogger logger;
|
||||
|
||||
explicit ConstraintSolver(TypeArena* arena, NotNull<Scope2> rootScope);
|
||||
explicit ConstraintSolver(TypeArena* arena, NotNull<Scope> rootScope);
|
||||
|
||||
/**
|
||||
* Attempts to dispatch all pending constraints and reach a type solution
|
||||
|
@ -62,6 +84,7 @@ struct ConstraintSolver
|
|||
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 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);
|
||||
/**
|
||||
|
@ -102,6 +125,11 @@ struct ConstraintSolver
|
|||
*/
|
||||
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:
|
||||
/**
|
||||
* Marks a constraint as being blocked on a type or type pack. The constraint
|
||||
|
@ -121,6 +149,6 @@ private:
|
|||
void unblock_(BlockedConstraintId progressed);
|
||||
};
|
||||
|
||||
void dump(NotNull<Scope2> rootScope, struct ToStringOptions& opts);
|
||||
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -15,8 +15,8 @@ namespace Luau
|
|||
struct ConstraintSolverLogger
|
||||
{
|
||||
std::string compileOutput();
|
||||
void captureBoundarySnapshot(const Scope2* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||
void prepareStepSnapshot(const Scope2* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||
void captureBoundarySnapshot(const Scope* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||
void prepareStepSnapshot(const Scope* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
|
||||
void commitPreparedStepSnapshot();
|
||||
|
||||
private:
|
||||
|
|
|
@ -152,7 +152,9 @@ struct Frontend
|
|||
void registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)>);
|
||||
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);
|
||||
|
||||
NotNull<Scope2> getGlobalScope2();
|
||||
LoadDefinitionFileResult loadDefinitionFile(std::string_view source, const std::string& packageName);
|
||||
|
||||
NotNull<Scope> getGlobalScope();
|
||||
|
||||
private:
|
||||
ModulePtr check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope);
|
||||
|
@ -169,7 +171,7 @@ private:
|
|||
std::unordered_map<std::string, ScopePtr> environments;
|
||||
std::unordered_map<std::string, std::function<void(TypeChecker&, ScopePtr)>> builtinDefinitions;
|
||||
|
||||
std::unique_ptr<Scope2> globalScope2;
|
||||
ScopePtr globalScope;
|
||||
|
||||
public:
|
||||
FileResolver* fileResolver;
|
||||
|
@ -180,6 +182,7 @@ public:
|
|||
ConfigResolver* configResolver;
|
||||
FrontendOptions options;
|
||||
InternalErrorReporter iceHandler;
|
||||
TypeArena globalTypes;
|
||||
TypeArena arenaForAutocomplete;
|
||||
|
||||
std::unordered_map<ModuleName, SourceNode> sourceNodes;
|
||||
|
|
235
Analysis/include/Luau/JsonEmitter.h
Normal file
235
Analysis/include/Luau/JsonEmitter.h
Normal 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
|
|
@ -52,6 +52,7 @@ struct LintWarning
|
|||
Code_DuplicateCondition = 24,
|
||||
Code_MisleadingAndOr = 25,
|
||||
Code_CommentDirective = 26,
|
||||
Code_IntegerParsing = 27,
|
||||
|
||||
Code__Count
|
||||
};
|
||||
|
|
|
@ -68,8 +68,7 @@ struct Module
|
|||
std::shared_ptr<Allocator> allocator;
|
||||
std::shared_ptr<AstNameTable> names;
|
||||
|
||||
std::vector<std::pair<Location, ScopePtr>> scopes; // never empty
|
||||
std::vector<std::pair<Location, std::unique_ptr<Scope2>>> scope2s; // never empty
|
||||
std::vector<std::pair<Location, ScopePtr>> scopes; // never empty
|
||||
|
||||
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
|
||||
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
|
||||
|
@ -86,7 +85,6 @@ struct Module
|
|||
bool timeout = false;
|
||||
|
||||
ScopePtr getModuleScope() const;
|
||||
Scope2* getModuleScope2() const;
|
||||
|
||||
// Once a module has been typechecked, we clone its public interface into a separate arena.
|
||||
// This helps us to force TypeVar ownership into a DAG rather than a DCG.
|
||||
|
|
|
@ -7,9 +7,9 @@ namespace Luau
|
|||
{
|
||||
|
||||
struct TypeArena;
|
||||
struct Scope2;
|
||||
struct Scope;
|
||||
|
||||
void quantify(TypeId ty, TypeLevel level);
|
||||
TypeId quantify(TypeArena* arena, TypeId ty, Scope2* scope);
|
||||
TypeId quantify(TypeArena* arena, TypeId ty, Scope* scope);
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -32,10 +32,16 @@ struct Scope
|
|||
explicit Scope(const ScopePtr& parent, int subLevel = 0); // child scope. Parent must not be nullptr.
|
||||
|
||||
const ScopePtr parent; // null for the root
|
||||
|
||||
// All the children of this scope.
|
||||
std::vector<NotNull<Scope>> children;
|
||||
std::unordered_map<Symbol, Binding> bindings;
|
||||
std::unordered_map<Name, TypeFun> typeBindings;
|
||||
std::unordered_map<Name, TypePackId> typePackBindings;
|
||||
TypePackId returnType;
|
||||
bool breakOk = false;
|
||||
std::optional<TypePackId> varargPack;
|
||||
// All constraints belonging to this scope.
|
||||
std::vector<ConstraintPtr> constraints;
|
||||
|
||||
TypeLevel level;
|
||||
|
||||
|
@ -45,7 +51,9 @@ struct Scope
|
|||
|
||||
std::unordered_map<Name, std::unordered_map<Name, TypeFun>> importedTypeBindings;
|
||||
|
||||
std::optional<TypeId> lookup(const Symbol& name);
|
||||
std::optional<TypeId> lookup(Symbol sym);
|
||||
std::optional<TypeFun> lookupTypeBinding(const Name& name);
|
||||
std::optional<TypePackId> lookupTypePackBinding(const Name& name);
|
||||
|
||||
std::optional<TypeFun> lookupType(const Name& name);
|
||||
std::optional<TypeFun> lookupImportedType(const Name& moduleAlias, const Name& name);
|
||||
|
@ -66,24 +74,4 @@ struct Scope
|
|||
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
|
||||
};
|
||||
|
||||
struct Scope2
|
||||
{
|
||||
// The parent scope of this scope. Null if there is no parent (i.e. this
|
||||
// is the module-level scope).
|
||||
Scope2* parent = nullptr;
|
||||
// All the children of this scope.
|
||||
std::vector<NotNull<Scope2>> children;
|
||||
std::unordered_map<Symbol, TypeId> bindings; // TODO: I think this can be a DenseHashMap
|
||||
std::unordered_map<Name, TypeId> typeBindings;
|
||||
std::unordered_map<Name, TypePackId> typePackBindings;
|
||||
TypePackId returnType;
|
||||
std::optional<TypePackId> varargPack;
|
||||
// All constraints belonging to this scope.
|
||||
std::vector<ConstraintPtr> constraints;
|
||||
|
||||
std::optional<TypeId> lookup(Symbol sym);
|
||||
std::optional<TypeId> lookupTypeBinding(const Name& name);
|
||||
std::optional<TypePackId> lookupTypePackBinding(const Name& name);
|
||||
};
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -139,6 +139,8 @@ struct FindDirty : Tarjan
|
|||
{
|
||||
std::vector<bool> dirty;
|
||||
|
||||
void clearTarjan();
|
||||
|
||||
// Get/set the dirty bit for an index (grows the vector if needed)
|
||||
bool getDirty(int index);
|
||||
void setDirty(int index, bool d);
|
||||
|
@ -176,6 +178,8 @@ public:
|
|||
TypeArena* arena;
|
||||
DenseHashMap<TypeId, TypeId> newTypes{nullptr};
|
||||
DenseHashMap<TypePackId, TypePackId> newPacks{nullptr};
|
||||
DenseHashSet<TypeId> replacedTypes{nullptr};
|
||||
DenseHashSet<TypePackId> replacedTypePacks{nullptr};
|
||||
|
||||
std::optional<TypeId> substitute(TypeId ty);
|
||||
std::optional<TypePackId> substitute(TypePackId tp);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
std::vector<GenericTypeDefinition> genericTypes;
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Luau
|
|||
{
|
||||
|
||||
struct TypeArena;
|
||||
struct Scope2;
|
||||
struct Scope;
|
||||
|
||||
/**
|
||||
* There are three kinds of type variables:
|
||||
|
@ -143,7 +143,7 @@ struct ConstrainedTypeVar
|
|||
|
||||
std::vector<TypeId> parts;
|
||||
TypeLevel level;
|
||||
Scope2* scope = nullptr;
|
||||
Scope* scope = nullptr;
|
||||
};
|
||||
|
||||
// Singleton types https://github.com/Roblox/luau/blob/master/rfcs/syntax-singleton-types.md
|
||||
|
@ -223,12 +223,16 @@ struct GenericTypeDefinition
|
|||
{
|
||||
TypeId ty;
|
||||
std::optional<TypeId> defaultValue;
|
||||
|
||||
bool operator==(const GenericTypeDefinition& rhs) const;
|
||||
};
|
||||
|
||||
struct GenericTypePackDefinition
|
||||
{
|
||||
TypePackId tp;
|
||||
std::optional<TypePackId> defaultValue;
|
||||
|
||||
bool operator==(const GenericTypePackDefinition& rhs) const;
|
||||
};
|
||||
|
||||
struct FunctionArgument
|
||||
|
@ -275,7 +279,7 @@ struct FunctionTypeVar
|
|||
std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
|
||||
|
||||
TypeLevel level;
|
||||
Scope2* scope = nullptr;
|
||||
Scope* scope = nullptr;
|
||||
/// These should all be generic
|
||||
std::vector<TypeId> generics;
|
||||
std::vector<TypePackId> genericPacks;
|
||||
|
@ -344,7 +348,7 @@ struct TableTypeVar
|
|||
|
||||
TableState state = TableState::Unsealed;
|
||||
TypeLevel level;
|
||||
Scope2* scope = nullptr;
|
||||
Scope* scope = nullptr;
|
||||
std::optional<std::string> name;
|
||||
|
||||
// Sometimes we throw a type on a name to make for nicer error messages, but without creating any entry in the type namespace
|
||||
|
@ -426,6 +430,12 @@ struct TypeFun
|
|||
TypeId type;
|
||||
|
||||
TypeFun() = default;
|
||||
|
||||
explicit TypeFun(TypeId ty)
|
||||
: type(ty)
|
||||
{
|
||||
}
|
||||
|
||||
TypeFun(std::vector<GenericTypeDefinition> typeParams, TypeId type)
|
||||
: typeParams(std::move(typeParams))
|
||||
, type(type)
|
||||
|
@ -438,6 +448,27 @@ struct TypeFun
|
|||
, 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.
|
||||
|
@ -470,8 +501,10 @@ struct NeverTypeVar
|
|||
|
||||
using ErrorTypeVar = Unifiable::Error;
|
||||
|
||||
using TypeVariant = Unifiable::Variant<TypeId, PrimitiveTypeVar, ConstrainedTypeVar, BlockedTypeVar, SingletonTypeVar, FunctionTypeVar, TableTypeVar,
|
||||
MetatableTypeVar, ClassTypeVar, AnyTypeVar, UnionTypeVar, IntersectionTypeVar, LazyTypeVar, UnknownTypeVar, NeverTypeVar>;
|
||||
using TypeVariant =
|
||||
Unifiable::Variant<TypeId, PrimitiveTypeVar, ConstrainedTypeVar, BlockedTypeVar, PendingExpansionTypeVar, SingletonTypeVar, FunctionTypeVar,
|
||||
TableTypeVar, MetatableTypeVar, ClassTypeVar, AnyTypeVar, UnionTypeVar, IntersectionTypeVar, LazyTypeVar, UnknownTypeVar, NeverTypeVar>;
|
||||
|
||||
|
||||
struct TypeVar final
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
struct Scope2;
|
||||
struct Scope;
|
||||
|
||||
/**
|
||||
* The 'level' of a TypeVar is an indirect way to talk about the scope that it 'belongs' too.
|
||||
|
@ -84,11 +84,11 @@ using Name = std::string;
|
|||
struct Free
|
||||
{
|
||||
explicit Free(TypeLevel level);
|
||||
explicit Free(Scope2* scope);
|
||||
explicit Free(Scope* scope);
|
||||
|
||||
int index;
|
||||
TypeLevel level;
|
||||
Scope2* scope = nullptr;
|
||||
Scope* scope = nullptr;
|
||||
// True if this free type variable is part of a mutually
|
||||
// recursive type alias whose definitions haven't been
|
||||
// resolved yet.
|
||||
|
@ -115,13 +115,13 @@ struct Generic
|
|||
Generic();
|
||||
explicit Generic(TypeLevel level);
|
||||
explicit Generic(const Name& name);
|
||||
explicit Generic(Scope2* scope);
|
||||
explicit Generic(Scope* scope);
|
||||
Generic(TypeLevel level, const Name& name);
|
||||
Generic(Scope2* scope, const Name& name);
|
||||
Generic(Scope* scope, const Name& name);
|
||||
|
||||
int index;
|
||||
TypeLevel level;
|
||||
Scope2* scope = nullptr;
|
||||
Scope* scope = nullptr;
|
||||
Name name;
|
||||
bool explicitName = false;
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "Luau/TypeVar.h"
|
||||
|
||||
LUAU_FASTINT(LuauVisitRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
|
||||
LUAU_FASTFLAG(LuauCompleteVisitor);
|
||||
|
||||
namespace Luau
|
||||
|
@ -150,6 +149,10 @@ struct GenericTypeVarVisitor
|
|||
{
|
||||
return visit(ty);
|
||||
}
|
||||
virtual bool visit(TypeId ty, const PendingExpansionTypeVar& petv)
|
||||
{
|
||||
return visit(ty);
|
||||
}
|
||||
virtual bool visit(TypeId ty, const SingletonTypeVar& stv)
|
||||
{
|
||||
return visit(ty);
|
||||
|
@ -285,8 +288,6 @@ struct GenericTypeVarVisitor
|
|||
traverse(partTy);
|
||||
}
|
||||
}
|
||||
else if (!FFlag::LuauCompleteVisitor)
|
||||
return visit_detail::unsee(seen, ty);
|
||||
else if (get<LazyTypeVar>(ty))
|
||||
{
|
||||
// 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);
|
||||
else if (auto ntv = get<NeverTypeVar>(ty))
|
||||
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
|
||||
LUAU_ASSERT(!"GenericTypeVarVisitor::traverse(TypeId) is not exhaustive!");
|
||||
|
||||
|
@ -333,7 +365,7 @@ struct GenericTypeVarVisitor
|
|||
else if (auto pack = get<TypePack>(tp))
|
||||
{
|
||||
bool res = visit(tp, *pack);
|
||||
if (!FFlag::LuauNormalizeFlagIsConservative || res)
|
||||
if (res)
|
||||
{
|
||||
for (TypeId ty : pack->head)
|
||||
traverse(ty);
|
||||
|
@ -345,7 +377,7 @@ struct GenericTypeVarVisitor
|
|||
else if (auto pack = get<VariadicTypePack>(tp))
|
||||
{
|
||||
bool res = visit(tp, *pack);
|
||||
if (!FFlag::LuauNormalizeFlagIsConservative || res)
|
||||
if (res)
|
||||
traverse(pack->ty);
|
||||
}
|
||||
else
|
||||
|
|
60
Analysis/src/ApplyTypeFunction.cpp
Normal file
60
Analysis/src/ApplyTypeFunction.cpp
Normal 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
|
|
@ -1,5 +1,5 @@
|
|||
// 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/ParseResult.h"
|
||||
|
@ -103,8 +103,8 @@ struct AstJsonEncoder : public AstVisitor
|
|||
|
||||
void write(double d)
|
||||
{
|
||||
char b[256];
|
||||
sprintf(b, "%.17g", d);
|
||||
char b[32];
|
||||
snprintf(b, sizeof(b), "%.17g", d);
|
||||
writeRaw(b);
|
||||
}
|
||||
|
|
@ -314,7 +314,7 @@ std::optional<Binding> findBindingAtPosition(const Module& module, const SourceM
|
|||
auto iter = currentScope->bindings.find(name);
|
||||
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);
|
||||
if (!bindingStatement || !(*bindingStatement)->location.contains(pos))
|
||||
return iter->second;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
LUAU_FASTFLAG(LuauSelfCallAutocompleteFix2)
|
||||
LUAU_FASTFLAG(LuauSelfCallAutocompleteFix3)
|
||||
|
||||
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
||||
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||
|
@ -149,7 +149,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
|||
ty = follow(ty);
|
||||
|
||||
auto canUnify = [&typeArena](TypeId subTy, TypeId superTy) {
|
||||
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix2);
|
||||
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
|
||||
|
||||
InternalErrorReporter iceReporter;
|
||||
UnifierSharedState unifierState(&iceReporter);
|
||||
|
@ -168,7 +168,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
|||
TypeId expectedType = follow(*typeAtPosition);
|
||||
|
||||
auto checkFunctionType = [typeArena, &canUnify, &expectedType](const FunctionTypeVar* ftv) {
|
||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
{
|
||||
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
|
||||
return checkTypeMatch(typeArena, *firstRetTy, expectedType);
|
||||
|
@ -209,7 +209,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
return checkTypeMatch(typeArena, ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||
else
|
||||
return canUnify(ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
|
||||
|
@ -226,7 +226,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
const std::vector<AstNode*>& nodes, AutocompleteEntryMap& result, std::unordered_set<TypeId>& seen,
|
||||
std::optional<const ClassTypeVar*> containingClass = std::nullopt)
|
||||
{
|
||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
rootTy = follow(rootTy);
|
||||
|
||||
ty = follow(ty);
|
||||
|
@ -236,7 +236,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
seen.insert(ty);
|
||||
|
||||
auto isWrongIndexer_DEPRECATED = [indexType, useStrictFunctionIndexers = !!get<ClassTypeVar>(ty)](Luau::TypeId type) {
|
||||
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix2);
|
||||
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
|
||||
|
||||
if (indexType == PropIndexType::Key)
|
||||
return false;
|
||||
|
@ -269,7 +269,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
}
|
||||
};
|
||||
auto isWrongIndexer = [typeArena, rootTy, indexType](Luau::TypeId type) {
|
||||
LUAU_ASSERT(FFlag::LuauSelfCallAutocompleteFix2);
|
||||
LUAU_ASSERT(FFlag::LuauSelfCallAutocompleteFix3);
|
||||
|
||||
if (indexType == PropIndexType::Key)
|
||||
return false;
|
||||
|
@ -277,21 +277,20 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
bool calledWithSelf = indexType == PropIndexType::Colon;
|
||||
|
||||
auto isCompatibleCall = [typeArena, rootTy, calledWithSelf](const FunctionTypeVar* ftv) {
|
||||
if (get<ClassTypeVar>(rootTy))
|
||||
{
|
||||
// Calls on classes require strict match between how function is declared and how it's called
|
||||
return calledWithSelf == ftv->hasSelf;
|
||||
}
|
||||
// Strong match with definition is a success
|
||||
if (calledWithSelf == ftv->hasSelf)
|
||||
return true;
|
||||
|
||||
// If a call is made with ':', it is invalid if a function has incompatible first argument or no arguments at all
|
||||
// If a call is made with '.', but it was declared with 'self', it is considered invalid if first argument is compatible
|
||||
if (calledWithSelf || ftv->hasSelf)
|
||||
// Calls on classes require strict match between how function is declared and how it's called
|
||||
if (get<ClassTypeVar>(rootTy))
|
||||
return false;
|
||||
|
||||
// When called with ':', but declared without 'self', it is invalid if a function has incompatible first argument or no arguments at all
|
||||
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
|
||||
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
|
||||
{
|
||||
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
|
||||
{
|
||||
if (checkTypeMatch(typeArena, rootTy, *firstArgTy))
|
||||
return calledWithSelf;
|
||||
}
|
||||
if (checkTypeMatch(typeArena, rootTy, *firstArgTy))
|
||||
return calledWithSelf;
|
||||
}
|
||||
|
||||
return !calledWithSelf;
|
||||
|
@ -333,7 +332,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
AutocompleteEntryKind::Property,
|
||||
type,
|
||||
prop.deprecated,
|
||||
FFlag::LuauSelfCallAutocompleteFix2 ? isWrongIndexer(type) : isWrongIndexer_DEPRECATED(type),
|
||||
FFlag::LuauSelfCallAutocompleteFix3 ? isWrongIndexer(type) : isWrongIndexer_DEPRECATED(type),
|
||||
typeCorrect,
|
||||
containingClass,
|
||||
&prop,
|
||||
|
@ -376,7 +375,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
{
|
||||
autocompleteProps(module, typeArena, rootTy, mt->table, indexType, nodes, result, seen);
|
||||
|
||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
{
|
||||
if (auto mtable = get<TableTypeVar>(mt->metatable))
|
||||
fillMetatableProps(mtable);
|
||||
|
@ -442,7 +441,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
AutocompleteEntryMap inner;
|
||||
std::unordered_set<TypeId> innerSeen;
|
||||
|
||||
if (!FFlag::LuauSelfCallAutocompleteFix2)
|
||||
if (!FFlag::LuauSelfCallAutocompleteFix3)
|
||||
innerSeen = seen;
|
||||
|
||||
if (isNil(*iter))
|
||||
|
@ -468,7 +467,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
++iter;
|
||||
}
|
||||
}
|
||||
else if (auto pt = get<PrimitiveTypeVar>(ty); pt && FFlag::LuauSelfCallAutocompleteFix2)
|
||||
else if (auto pt = get<PrimitiveTypeVar>(ty); pt && FFlag::LuauSelfCallAutocompleteFix3)
|
||||
{
|
||||
if (pt->metatable)
|
||||
{
|
||||
|
@ -476,7 +475,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
fillMetatableProps(mtable);
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauSelfCallAutocompleteFix2 && get<StringSingleton>(get<SingletonTypeVar>(ty)))
|
||||
else if (FFlag::LuauSelfCallAutocompleteFix3 && get<StringSingleton>(get<SingletonTypeVar>(ty)))
|
||||
{
|
||||
autocompleteProps(module, typeArena, rootTy, getSingletonTypes().stringType, indexType, nodes, result, seen);
|
||||
}
|
||||
|
@ -1405,7 +1404,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
|||
TypeId ty = follow(*it);
|
||||
PropIndexType indexType = indexName->op == ':' ? PropIndexType::Colon : PropIndexType::Point;
|
||||
|
||||
if (!FFlag::LuauSelfCallAutocompleteFix2 && isString(ty))
|
||||
if (!FFlag::LuauSelfCallAutocompleteFix3 && isString(ty))
|
||||
return {
|
||||
autocompleteProps(*module, typeArena, typeChecker.globalScope->bindings[AstName{"string"}].typeId, indexType, ancestry), ancestry};
|
||||
else
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
LUAU_FASTFLAGVARIABLE(LuauSetMetaTableArgsCheck, false)
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBuiltInMetatableNoBadSynthetic, false)
|
||||
|
||||
/** FIXME: Many of these type definitions are not quite completely accurate.
|
||||
*
|
||||
|
@ -349,7 +350,7 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
|
|||
|
||||
if (tableName == metatableName)
|
||||
mtv.syntheticName = tableName;
|
||||
else
|
||||
else if (!FFlag::LuauBuiltInMetatableNoBadSynthetic)
|
||||
mtv.syntheticName = "{ @metatable: " + metatableName + ", " + tableName + " }";
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ struct TypeCloner
|
|||
void operator()(const Unifiable::Bound<TypeId>& t);
|
||||
void operator()(const Unifiable::Error& t);
|
||||
void operator()(const BlockedTypeVar& t);
|
||||
void operator()(const PendingExpansionTypeVar& t);
|
||||
void operator()(const PrimitiveTypeVar& t);
|
||||
void operator()(const ConstrainedTypeVar& t);
|
||||
void operator()(const SingletonTypeVar& t);
|
||||
|
@ -166,6 +167,52 @@ void TypeCloner::operator()(const BlockedTypeVar& 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)
|
||||
{
|
||||
defaultClone(t);
|
||||
|
@ -452,6 +499,11 @@ TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
|
|||
ConstrainedTypeVar clone{ctv->level, ctv->parts};
|
||||
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
|
||||
return result;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Luau
|
|||
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
|
||||
|
||||
ConstraintGraphBuilder::ConstraintGraphBuilder(
|
||||
const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope2> globalScope)
|
||||
const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope> globalScope)
|
||||
: moduleName(moduleName)
|
||||
, singletonTypes(getSingletonTypes())
|
||||
, arena(arena)
|
||||
|
@ -25,36 +25,34 @@ ConstraintGraphBuilder::ConstraintGraphBuilder(
|
|||
LUAU_ASSERT(arena);
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::freshType(NotNull<Scope2> scope)
|
||||
TypeId ConstraintGraphBuilder::freshType(const ScopePtr& scope)
|
||||
{
|
||||
return arena->addType(FreeTypeVar{scope});
|
||||
return arena->addType(FreeTypeVar{scope.get()});
|
||||
}
|
||||
|
||||
TypePackId ConstraintGraphBuilder::freshTypePack(NotNull<Scope2> scope)
|
||||
TypePackId ConstraintGraphBuilder::freshTypePack(const ScopePtr& scope)
|
||||
{
|
||||
FreeTypePack f{scope};
|
||||
FreeTypePack f{scope.get()};
|
||||
return arena->addTypePack(TypePackVar{std::move(f)});
|
||||
}
|
||||
|
||||
NotNull<Scope2> ConstraintGraphBuilder::childScope(Location location, NotNull<Scope2> parent)
|
||||
ScopePtr ConstraintGraphBuilder::childScope(Location location, const ScopePtr& parent)
|
||||
{
|
||||
auto scope = std::make_unique<Scope2>();
|
||||
NotNull<Scope2> borrow = NotNull(scope.get());
|
||||
scopes.emplace_back(location, std::move(scope));
|
||||
auto scope = std::make_shared<Scope>(parent);
|
||||
scopes.emplace_back(location, scope);
|
||||
|
||||
borrow->parent = parent;
|
||||
borrow->returnType = parent->returnType;
|
||||
parent->children.push_back(borrow);
|
||||
scope->returnType = parent->returnType;
|
||||
parent->children.push_back(NotNull(scope.get()));
|
||||
|
||||
return borrow;
|
||||
return scope;
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::addConstraint(NotNull<Scope2> scope, ConstraintV cv)
|
||||
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, ConstraintV cv)
|
||||
{
|
||||
scope->constraints.emplace_back(new Constraint{std::move(cv)});
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::addConstraint(NotNull<Scope2> scope, std::unique_ptr<Constraint> c)
|
||||
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c)
|
||||
{
|
||||
scope->constraints.emplace_back(std::move(c));
|
||||
}
|
||||
|
@ -63,25 +61,25 @@ void ConstraintGraphBuilder::visit(AstStatBlock* block)
|
|||
{
|
||||
LUAU_ASSERT(scopes.empty());
|
||||
LUAU_ASSERT(rootScope == nullptr);
|
||||
scopes.emplace_back(block->location, std::make_unique<Scope2>());
|
||||
rootScope = scopes.back().second.get();
|
||||
NotNull<Scope2> borrow = NotNull(rootScope);
|
||||
ScopePtr scope = std::make_shared<Scope>(singletonTypes.anyTypePack);
|
||||
rootScope = scope.get();
|
||||
scopes.emplace_back(block->location, scope);
|
||||
|
||||
rootScope->returnType = freshTypePack(borrow);
|
||||
rootScope->returnType = freshTypePack(scope);
|
||||
|
||||
prepopulateGlobalScope(borrow, block);
|
||||
prepopulateGlobalScope(scope, block);
|
||||
|
||||
// TODO: We should share the global scope.
|
||||
rootScope->typeBindings["nil"] = singletonTypes.nilType;
|
||||
rootScope->typeBindings["number"] = singletonTypes.numberType;
|
||||
rootScope->typeBindings["string"] = singletonTypes.stringType;
|
||||
rootScope->typeBindings["boolean"] = singletonTypes.booleanType;
|
||||
rootScope->typeBindings["thread"] = singletonTypes.threadType;
|
||||
rootScope->typeBindings["nil"] = TypeFun{singletonTypes.nilType};
|
||||
rootScope->typeBindings["number"] = TypeFun{singletonTypes.numberType};
|
||||
rootScope->typeBindings["string"] = TypeFun{singletonTypes.stringType};
|
||||
rootScope->typeBindings["boolean"] = TypeFun{singletonTypes.booleanType};
|
||||
rootScope->typeBindings["thread"] = TypeFun{singletonTypes.threadType};
|
||||
|
||||
visitBlockWithoutChildScope(borrow, block);
|
||||
visitBlockWithoutChildScope(scope, block);
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visitBlockWithoutChildScope(NotNull<Scope2> scope, AstStatBlock* block)
|
||||
void ConstraintGraphBuilder::visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block)
|
||||
{
|
||||
RecursionCounter counter{&recursionCount};
|
||||
|
||||
|
@ -91,11 +89,58 @@ void ConstraintGraphBuilder::visitBlockWithoutChildScope(NotNull<Scope2> scope,
|
|||
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)
|
||||
visit(scope, stat);
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStat* stat)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
|
||||
{
|
||||
RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit};
|
||||
|
||||
|
@ -119,26 +164,34 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStat* stat)
|
|||
visit(scope, i);
|
||||
else if (auto a = stat->as<AstStatTypeAlias>())
|
||||
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
|
||||
LUAU_ASSERT(0);
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocal* local)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
|
||||
{
|
||||
std::vector<TypeId> varTypes;
|
||||
|
||||
for (AstLocal* local : local->vars)
|
||||
{
|
||||
TypeId ty = freshType(scope);
|
||||
Location location = local->location;
|
||||
|
||||
if (local->annotation)
|
||||
{
|
||||
TypeId annotation = resolveType(scope, local->annotation);
|
||||
location = local->annotation->location;
|
||||
TypeId annotation = resolveType(scope, local->annotation, /* topLevel */ true);
|
||||
addConstraint(scope, SubtypeConstraint{ty, annotation});
|
||||
}
|
||||
|
||||
varTypes.push_back(ty);
|
||||
scope->bindings[local] = ty;
|
||||
scope->bindings[local] = Binding{ty, location};
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < local->values.size; ++i)
|
||||
|
@ -169,13 +222,12 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocal* local)
|
|||
}
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFor* for_)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
|
||||
{
|
||||
auto checkNumber = [&](AstExpr* expr)
|
||||
{
|
||||
auto checkNumber = [&](AstExpr* expr) {
|
||||
if (!expr)
|
||||
return;
|
||||
|
||||
|
||||
TypeId t = check(scope, expr);
|
||||
addConstraint(scope, SubtypeConstraint{t, singletonTypes.numberType});
|
||||
};
|
||||
|
@ -184,24 +236,24 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFor* for_)
|
|||
checkNumber(for_->to);
|
||||
checkNumber(for_->step);
|
||||
|
||||
NotNull<Scope2> forScope = childScope(for_->location, scope);
|
||||
forScope->bindings[for_->var] = singletonTypes.numberType;
|
||||
ScopePtr forScope = childScope(for_->location, scope);
|
||||
forScope->bindings[for_->var] = Binding{singletonTypes.numberType, for_->var->location};
|
||||
|
||||
visit(forScope, for_->body);
|
||||
}
|
||||
|
||||
void addConstraints(Constraint* constraint, NotNull<Scope2> scope)
|
||||
void addConstraints(Constraint* constraint, NotNull<Scope> scope)
|
||||
{
|
||||
scope->constraints.reserve(scope->constraints.size() + scope->constraints.size());
|
||||
|
||||
for (const auto& c : scope->constraints)
|
||||
constraint->dependencies.push_back(NotNull{c.get()});
|
||||
|
||||
for (NotNull<Scope2> childScope : scope->children)
|
||||
for (NotNull<Scope> childScope : scope->children)
|
||||
addConstraints(constraint, childScope);
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocalFunction* function)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction* function)
|
||||
{
|
||||
// Local
|
||||
// Global
|
||||
|
@ -213,21 +265,21 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocalFunction*
|
|||
LUAU_ASSERT(!ty.has_value()); // The parser ensures that every local function has a distinct Symbol for its name.
|
||||
|
||||
functionType = arena->addType(BlockedTypeVar{});
|
||||
scope->bindings[function->name] = functionType;
|
||||
scope->bindings[function->name] = Binding{functionType, function->name->location};
|
||||
|
||||
FunctionSignature sig = checkFunctionSignature(scope, function->func);
|
||||
sig.bodyScope->bindings[function->name] = sig.signature;
|
||||
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->func->location};
|
||||
|
||||
checkFunctionBody(sig.bodyScope, function->func);
|
||||
|
||||
std::unique_ptr<Constraint> c{
|
||||
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope : sig.bodyScope}}};
|
||||
addConstraints(c.get(), sig.bodyScope);
|
||||
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
|
||||
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
|
||||
|
||||
addConstraint(scope, std::move(c));
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* function)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* function)
|
||||
{
|
||||
// Name could be AstStatLocal, AstStatGlobal, AstStatIndexName.
|
||||
// With or without self
|
||||
|
@ -247,9 +299,9 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* funct
|
|||
else
|
||||
{
|
||||
functionType = arena->addType(BlockedTypeVar{});
|
||||
scope->bindings[localName->local] = functionType;
|
||||
scope->bindings[localName->local] = Binding{functionType, localName->location};
|
||||
}
|
||||
sig.bodyScope->bindings[localName->local] = sig.signature;
|
||||
sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location};
|
||||
}
|
||||
else if (AstExprGlobal* globalName = function->name->as<AstExprGlobal>())
|
||||
{
|
||||
|
@ -262,9 +314,9 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* funct
|
|||
else
|
||||
{
|
||||
functionType = arena->addType(BlockedTypeVar{});
|
||||
rootScope->bindings[globalName->name] = functionType;
|
||||
rootScope->bindings[globalName->name] = Binding{functionType, globalName->location};
|
||||
}
|
||||
sig.bodyScope->bindings[globalName->name] = sig.signature;
|
||||
sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location};
|
||||
}
|
||||
else if (AstExprIndexName* indexName = function->name->as<AstExprIndexName>())
|
||||
{
|
||||
|
@ -291,39 +343,26 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* funct
|
|||
checkFunctionBody(sig.bodyScope, function->func);
|
||||
|
||||
std::unique_ptr<Constraint> c{
|
||||
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope : sig.bodyScope}}};
|
||||
addConstraints(c.get(), sig.bodyScope);
|
||||
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
|
||||
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
|
||||
|
||||
addConstraint(scope, std::move(c));
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatReturn* ret)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatReturn* ret)
|
||||
{
|
||||
TypePackId exprTypes = checkPack(scope, ret->list);
|
||||
addConstraint(scope, PackSubtypeConstraint{exprTypes, scope->returnType});
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatBlock* block)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
|
||||
{
|
||||
NotNull<Scope2> 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;
|
||||
}
|
||||
}
|
||||
ScopePtr innerScope = childScope(block->location, scope);
|
||||
|
||||
visitBlockWithoutChildScope(innerScope, block);
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatAssign* assign)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatAssign* assign)
|
||||
{
|
||||
TypePackId varPackId = checkExprList(scope, assign->vars);
|
||||
TypePackId valuePack = checkPack(scope, assign->values);
|
||||
|
@ -331,47 +370,66 @@ void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatAssign* assign)
|
|||
addConstraint(scope, PackSubtypeConstraint{valuePack, varPackId});
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatIf* ifStatement)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatIf* ifStatement)
|
||||
{
|
||||
check(scope, ifStatement->condition);
|
||||
|
||||
NotNull<Scope2> thenScope = childScope(ifStatement->thenbody->location, scope);
|
||||
ScopePtr thenScope = childScope(ifStatement->thenbody->location, scope);
|
||||
visit(thenScope, ifStatement->thenbody);
|
||||
|
||||
if (ifStatement->elsebody)
|
||||
{
|
||||
NotNull<Scope2> elseScope = childScope(ifStatement->elsebody->location, scope);
|
||||
ScopePtr elseScope = childScope(ifStatement->elsebody->location, scope);
|
||||
visit(elseScope, ifStatement->elsebody);
|
||||
}
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatTypeAlias* alias)
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alias)
|
||||
{
|
||||
// TODO: Exported type aliases
|
||||
// TODO: Generic type aliases
|
||||
|
||||
auto it = scope->typeBindings.find(alias->name.value);
|
||||
// This should always be here since we do a separate pass over the
|
||||
// AST to set up typeBindings. If it's not, we've somehow skipped
|
||||
// this alias in that first pass.
|
||||
LUAU_ASSERT(it != scope->typeBindings.end());
|
||||
if (it == scope->typeBindings.end())
|
||||
auto bindingIt = scope->typeBindings.find(alias->name.value);
|
||||
ScopePtr* defnIt = astTypeAliasDefiningScopes.find(alias);
|
||||
// These will be undefined if the alias was a duplicate definition, in which
|
||||
// case we just skip over it.
|
||||
if (bindingIt == scope->typeBindings.end() || defnIt == nullptr)
|
||||
{
|
||||
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
|
||||
// the free type we generated in the first pass to the resolved type.
|
||||
// This prevents a case where you could cause another constraint to
|
||||
// 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});
|
||||
}
|
||||
|
||||
TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstArray<AstExpr*> exprs)
|
||||
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)
|
||||
{
|
||||
if (exprs.size == 0)
|
||||
return arena->addTypePack({});
|
||||
|
@ -392,7 +450,7 @@ TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstArray<Ast
|
|||
return arena->addTypePack(TypePack{std::move(types), last});
|
||||
}
|
||||
|
||||
TypePackId ConstraintGraphBuilder::checkExprList(NotNull<Scope2> scope, const AstArray<AstExpr*>& exprs)
|
||||
TypePackId ConstraintGraphBuilder::checkExprList(const ScopePtr& scope, const AstArray<AstExpr*>& exprs)
|
||||
{
|
||||
TypePackId result = arena->addTypePack({});
|
||||
TypePack* resultPack = getMutable<TypePack>(result);
|
||||
|
@ -413,7 +471,7 @@ TypePackId ConstraintGraphBuilder::checkExprList(NotNull<Scope2> scope, const As
|
|||
return result;
|
||||
}
|
||||
|
||||
TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstExpr* expr)
|
||||
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* expr)
|
||||
{
|
||||
RecursionCounter counter{&recursionCount};
|
||||
|
||||
|
@ -468,7 +526,7 @@ TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstExpr* exp
|
|||
return result;
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExpr* expr)
|
||||
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
|
||||
{
|
||||
RecursionCounter counter{&recursionCount};
|
||||
|
||||
|
@ -548,7 +606,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExpr* expr)
|
|||
return result;
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexName* indexName)
|
||||
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexName* indexName)
|
||||
{
|
||||
TypeId obj = check(scope, indexName->expr);
|
||||
TypeId result = freshType(scope);
|
||||
|
@ -564,7 +622,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexName* in
|
|||
return result;
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexExpr* indexExpr)
|
||||
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexExpr* indexExpr)
|
||||
{
|
||||
TypeId obj = check(scope, indexExpr->expr);
|
||||
TypeId indexType = check(scope, indexExpr->index);
|
||||
|
@ -579,7 +637,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexExpr* in
|
|||
return result;
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprUnary* unary)
|
||||
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprUnary* unary)
|
||||
{
|
||||
TypeId operandType = check(scope, unary->expr);
|
||||
|
||||
|
@ -599,7 +657,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprUnary* unary)
|
|||
return singletonTypes.errorRecoveryType();
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprBinary* binary)
|
||||
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* binary)
|
||||
{
|
||||
TypeId leftType = check(scope, binary->left);
|
||||
TypeId rightType = check(scope, binary->right);
|
||||
|
@ -624,7 +682,7 @@ TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprBinary* binar
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::checkExprTable(NotNull<Scope2> scope, AstExprTable* expr)
|
||||
TypeId ConstraintGraphBuilder::checkExprTable(const ScopePtr& scope, AstExprTable* expr)
|
||||
{
|
||||
TypeId ty = arena->addType(TableTypeVar{});
|
||||
TableTypeVar* ttv = getMutable<TableTypeVar>(ty);
|
||||
|
@ -674,10 +732,10 @@ TypeId ConstraintGraphBuilder::checkExprTable(NotNull<Scope2> scope, AstExprTabl
|
|||
return ty;
|
||||
}
|
||||
|
||||
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(NotNull<Scope2> parent, AstExprFunction* fn)
|
||||
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn)
|
||||
{
|
||||
Scope2* signatureScope = nullptr;
|
||||
Scope2* bodyScope = nullptr;
|
||||
ScopePtr signatureScope = nullptr;
|
||||
ScopePtr bodyScope = nullptr;
|
||||
TypePackId returnType = nullptr;
|
||||
|
||||
std::vector<TypeId> genericTypes;
|
||||
|
@ -690,25 +748,24 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
|||
// generics properly.
|
||||
if (hasGenerics)
|
||||
{
|
||||
NotNull signatureBorrow = childScope(fn->location, parent);
|
||||
signatureScope = signatureBorrow.get();
|
||||
signatureScope = childScope(fn->location, parent);
|
||||
|
||||
// We need to assign returnType before creating bodyScope so that the
|
||||
// return type gets propogated to bodyScope.
|
||||
returnType = freshTypePack(signatureBorrow);
|
||||
returnType = freshTypePack(signatureScope);
|
||||
signatureScope->returnType = returnType;
|
||||
|
||||
bodyScope = childScope(fn->body->location, signatureBorrow).get();
|
||||
bodyScope = childScope(fn->body->location, signatureScope);
|
||||
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureBorrow, fn->generics);
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureBorrow, fn->genericPacks);
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
|
||||
|
||||
// We do not support default values on function generics, so we only
|
||||
// care about the types involved.
|
||||
for (const auto& [name, g] : genericDefinitions)
|
||||
{
|
||||
genericTypes.push_back(g.ty);
|
||||
signatureScope->typeBindings[name] = g.ty;
|
||||
signatureScope->typeBindings[name] = TypeFun{g.ty};
|
||||
}
|
||||
|
||||
for (const auto& [name, g] : genericPackDefinitions)
|
||||
|
@ -719,11 +776,10 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
|||
}
|
||||
else
|
||||
{
|
||||
NotNull bodyBorrow = childScope(fn->body->location, parent);
|
||||
bodyScope = bodyBorrow.get();
|
||||
bodyScope = childScope(fn->body->location, parent);
|
||||
|
||||
returnType = freshTypePack(bodyBorrow);
|
||||
bodyBorrow->returnType = returnType;
|
||||
returnType = freshTypePack(bodyScope);
|
||||
bodyScope->returnType = returnType;
|
||||
|
||||
// To eliminate the need to branch on hasGenerics below, we say that the
|
||||
// signature scope is the body scope when there is no real signature
|
||||
|
@ -731,27 +787,24 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
|||
signatureScope = bodyScope;
|
||||
}
|
||||
|
||||
NotNull bodyBorrow = NotNull(bodyScope);
|
||||
NotNull signatureBorrow = NotNull(signatureScope);
|
||||
|
||||
if (fn->returnAnnotation)
|
||||
{
|
||||
TypePackId annotatedRetType = resolveTypePack(signatureBorrow, *fn->returnAnnotation);
|
||||
addConstraint(signatureBorrow, PackSubtypeConstraint{returnType, annotatedRetType});
|
||||
TypePackId annotatedRetType = resolveTypePack(signatureScope, *fn->returnAnnotation);
|
||||
addConstraint(signatureScope, PackSubtypeConstraint{returnType, annotatedRetType});
|
||||
}
|
||||
|
||||
std::vector<TypeId> argTypes;
|
||||
|
||||
for (AstLocal* local : fn->args)
|
||||
{
|
||||
TypeId t = freshType(signatureBorrow);
|
||||
TypeId t = freshType(signatureScope);
|
||||
argTypes.push_back(t);
|
||||
signatureScope->bindings[local] = t;
|
||||
signatureScope->bindings[local] = Binding{t, local->location};
|
||||
|
||||
if (local->annotation)
|
||||
{
|
||||
TypeId argAnnotation = resolveType(signatureBorrow, local->annotation);
|
||||
addConstraint(signatureBorrow, SubtypeConstraint{t, argAnnotation});
|
||||
TypeId argAnnotation = resolveType(signatureScope, local->annotation, /* topLevel */ true);
|
||||
addConstraint(signatureScope, SubtypeConstraint{t, argAnnotation});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -772,11 +825,11 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
|||
// Undo the workaround we made above: if there's no signature scope,
|
||||
// don't report it.
|
||||
/* signatureScope */ hasGenerics ? signatureScope : nullptr,
|
||||
/* bodyScope */ bodyBorrow,
|
||||
/* bodyScope */ bodyScope,
|
||||
};
|
||||
}
|
||||
|
||||
void ConstraintGraphBuilder::checkFunctionBody(NotNull<Scope2> scope, AstExprFunction* fn)
|
||||
void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn)
|
||||
{
|
||||
visitBlockWithoutChildScope(scope, fn->body);
|
||||
|
||||
|
@ -789,20 +842,65 @@ void ConstraintGraphBuilder::checkFunctionBody(NotNull<Scope2> scope, AstExprFun
|
|||
}
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
||||
TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty, bool topLevel)
|
||||
{
|
||||
TypeId result = nullptr;
|
||||
|
||||
if (auto ref = ty->as<AstTypeReference>())
|
||||
{
|
||||
// TODO: Support imported types w/ require tracing.
|
||||
// TODO: Support generic type references.
|
||||
LUAU_ASSERT(!ref->prefix);
|
||||
LUAU_ASSERT(!ref->hasParameterList);
|
||||
|
||||
// TODO: If it doesn't exist, should we introduce a free binding?
|
||||
// This is probably important for handling type aliases.
|
||||
result = scope->lookupTypeBinding(ref->name.value).value_or(singletonTypes.errorRecoveryType());
|
||||
std::optional<TypeFun> alias = scope->lookupTypeBinding(ref->name.value);
|
||||
|
||||
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>())
|
||||
{
|
||||
|
@ -834,7 +932,7 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
|||
{
|
||||
// TODO: Recursion limit.
|
||||
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
|
||||
Scope2* signatureScope = nullptr;
|
||||
ScopePtr signatureScope = nullptr;
|
||||
|
||||
std::vector<TypeId> genericTypes;
|
||||
std::vector<TypePackId> genericTypePacks;
|
||||
|
@ -843,22 +941,21 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
|||
// for the generic bindings to live on.
|
||||
if (hasGenerics)
|
||||
{
|
||||
NotNull<Scope2> signatureBorrow = childScope(fn->location, scope);
|
||||
signatureScope = signatureBorrow.get();
|
||||
signatureScope = childScope(fn->location, scope);
|
||||
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureBorrow, fn->generics);
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureBorrow, fn->genericPacks);
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
|
||||
|
||||
for (const auto& [name, g] : genericDefinitions)
|
||||
{
|
||||
genericTypes.push_back(g.ty);
|
||||
signatureBorrow->typeBindings[name] = g.ty;
|
||||
signatureScope->typeBindings[name] = TypeFun{g.ty};
|
||||
}
|
||||
|
||||
for (const auto& [name, g] : genericPackDefinitions)
|
||||
{
|
||||
genericTypePacks.push_back(g.tp);
|
||||
signatureBorrow->typePackBindings[name] = g.tp;
|
||||
signatureScope->typePackBindings[name] = g.tp;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -866,13 +963,11 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
|||
// To eliminate the need to branch on hasGenerics below, we say that
|
||||
// the signature scope is the parent scope if we don't have
|
||||
// generics.
|
||||
signatureScope = scope.get();
|
||||
signatureScope = scope;
|
||||
}
|
||||
|
||||
NotNull<Scope2> signatureBorrow(signatureScope);
|
||||
|
||||
TypePackId argTypes = resolveTypePack(signatureBorrow, fn->argTypes);
|
||||
TypePackId returnTypes = resolveTypePack(signatureBorrow, fn->returnTypes);
|
||||
TypePackId argTypes = resolveTypePack(signatureScope, fn->argTypes);
|
||||
TypePackId returnTypes = resolveTypePack(signatureScope, fn->returnTypes);
|
||||
|
||||
// TODO: FunctionTypeVar needs a pointer to the scope so that we know
|
||||
// how to quantify/instantiate it.
|
||||
|
@ -950,7 +1045,7 @@ TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
|
|||
return result;
|
||||
}
|
||||
|
||||
TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTypePack* tp)
|
||||
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTypePack* tp)
|
||||
{
|
||||
TypePackId result;
|
||||
if (auto expl = tp->as<AstTypePackExplicit>())
|
||||
|
@ -964,7 +1059,15 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTyp
|
|||
}
|
||||
else if (auto gen = tp->as<AstTypePackGeneric>())
|
||||
{
|
||||
result = arena->addTypePack(TypePackVar{GenericTypePack{scope, 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
|
||||
{
|
||||
|
@ -976,7 +1079,7 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTyp
|
|||
return result;
|
||||
}
|
||||
|
||||
TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, const AstTypeList& list)
|
||||
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, const AstTypeList& list)
|
||||
{
|
||||
std::vector<TypeId> head;
|
||||
|
||||
|
@ -994,12 +1097,12 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, const
|
|||
return arena->addTypePack(TypePack{head, tail});
|
||||
}
|
||||
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::createGenerics(NotNull<Scope2> scope, AstArray<AstGenericType> generics)
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::createGenerics(const ScopePtr& scope, AstArray<AstGenericType> generics)
|
||||
{
|
||||
std::vector<std::pair<Name, GenericTypeDefinition>> result;
|
||||
for (const auto& generic : generics)
|
||||
{
|
||||
TypeId genericTy = arena->addType(GenericTypeVar{scope, generic.name.value});
|
||||
TypeId genericTy = arena->addType(GenericTypeVar{scope.get(), generic.name.value});
|
||||
std::optional<TypeId> defaultTy = std::nullopt;
|
||||
|
||||
if (generic.defaultValue)
|
||||
|
@ -1015,12 +1118,12 @@ std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::crea
|
|||
}
|
||||
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::createGenericPacks(
|
||||
NotNull<Scope2> scope, AstArray<AstGenericTypePack> generics)
|
||||
const ScopePtr& scope, AstArray<AstGenericTypePack> generics)
|
||||
{
|
||||
std::vector<std::pair<Name, GenericTypePackDefinition>> result;
|
||||
for (const auto& generic : generics)
|
||||
{
|
||||
TypePackId genericTy = arena->addTypePack(TypePackVar{GenericTypePack{scope, generic.name.value}});
|
||||
TypePackId genericTy = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), generic.name.value}});
|
||||
std::optional<TypePackId> defaultTy = std::nullopt;
|
||||
|
||||
if (generic.defaultValue)
|
||||
|
@ -1035,7 +1138,7 @@ std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::
|
|||
return result;
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::flattenPack(NotNull<Scope2> scope, Location location, TypePackId tp)
|
||||
TypeId ConstraintGraphBuilder::flattenPack(const ScopePtr& scope, Location location, TypePackId tp)
|
||||
{
|
||||
if (auto f = first(tp))
|
||||
return *f;
|
||||
|
@ -1061,10 +1164,10 @@ void ConstraintGraphBuilder::reportCodeTooComplex(Location location)
|
|||
|
||||
struct GlobalPrepopulator : AstVisitor
|
||||
{
|
||||
const NotNull<Scope2> globalScope;
|
||||
const NotNull<Scope> globalScope;
|
||||
const NotNull<TypeArena> arena;
|
||||
|
||||
GlobalPrepopulator(NotNull<Scope2> globalScope, NotNull<TypeArena> arena)
|
||||
GlobalPrepopulator(NotNull<Scope> globalScope, NotNull<TypeArena> arena)
|
||||
: globalScope(globalScope)
|
||||
, arena(arena)
|
||||
{
|
||||
|
@ -1073,29 +1176,29 @@ struct GlobalPrepopulator : AstVisitor
|
|||
bool visit(AstStatFunction* function) override
|
||||
{
|
||||
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
|
||||
globalScope->bindings[g->name] = arena->addType(BlockedTypeVar{});
|
||||
globalScope->bindings[g->name] = Binding{arena->addType(BlockedTypeVar{})};
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void ConstraintGraphBuilder::prepopulateGlobalScope(NotNull<Scope2> globalScope, AstStatBlock* program)
|
||||
void ConstraintGraphBuilder::prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program)
|
||||
{
|
||||
GlobalPrepopulator gp{NotNull{globalScope}, arena};
|
||||
GlobalPrepopulator gp{NotNull{globalScope.get()}, arena};
|
||||
|
||||
program->visit(&gp);
|
||||
}
|
||||
|
||||
void collectConstraints(std::vector<NotNull<Constraint>>& result, NotNull<Scope2> scope)
|
||||
void collectConstraints(std::vector<NotNull<Constraint>>& result, NotNull<Scope> scope)
|
||||
{
|
||||
for (const auto& c : scope->constraints)
|
||||
result.push_back(NotNull{c.get()});
|
||||
|
||||
for (NotNull<Scope2> child : scope->children)
|
||||
for (NotNull<Scope> child : scope->children)
|
||||
collectConstraints(result, child);
|
||||
}
|
||||
|
||||
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope2> rootScope)
|
||||
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope> rootScope)
|
||||
{
|
||||
std::vector<NotNull<Constraint>> result;
|
||||
collectConstraints(result, rootScope);
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
// 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/Instantiation.h"
|
||||
#include "Luau/Location.h"
|
||||
#include "Luau/Quantify.h"
|
||||
#include "Luau/ToString.h"
|
||||
#include "Luau/Unifier.h"
|
||||
#include "Luau/VisitTypeVar.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false);
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false);
|
||||
|
@ -13,31 +15,195 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false);
|
|||
namespace Luau
|
||||
{
|
||||
|
||||
[[maybe_unused]] static void dumpBindings(NotNull<Scope2> scope, ToStringOptions& opts)
|
||||
[[maybe_unused]] static void dumpBindings(NotNull<Scope> scope, ToStringOptions& opts)
|
||||
{
|
||||
for (const auto& [k, v] : scope->bindings)
|
||||
{
|
||||
auto d = toStringDetailed(v, opts);
|
||||
auto d = toStringDetailed(v.typeId, opts);
|
||||
opts.nameMap = d.nameMap;
|
||||
printf("\t%s : %s\n", k.c_str(), d.name.c_str());
|
||||
}
|
||||
|
||||
for (NotNull<Scope2> child : scope->children)
|
||||
for (NotNull<Scope> child : scope->children)
|
||||
dumpBindings(child, opts);
|
||||
}
|
||||
|
||||
static void dumpConstraints(NotNull<Scope2> scope, ToStringOptions& opts)
|
||||
static void dumpConstraints(NotNull<Scope> scope, ToStringOptions& opts)
|
||||
{
|
||||
for (const ConstraintPtr& c : scope->constraints)
|
||||
{
|
||||
printf("\t%s\n", toString(*c, opts).c_str());
|
||||
}
|
||||
|
||||
for (NotNull<Scope2> child : scope->children)
|
||||
for (NotNull<Scope> child : scope->children)
|
||||
dumpConstraints(child, opts);
|
||||
}
|
||||
|
||||
void dump(NotNull<Scope2> rootScope, ToStringOptions& 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)
|
||||
{
|
||||
printf("constraints:\n");
|
||||
dumpConstraints(rootScope, opts);
|
||||
|
@ -55,7 +221,7 @@ void dump(ConstraintSolver* cs, ToStringOptions& opts)
|
|||
}
|
||||
}
|
||||
|
||||
ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<Scope2> rootScope)
|
||||
ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<Scope> rootScope)
|
||||
: arena(arena)
|
||||
, constraints(collectConstraints(rootScope))
|
||||
, rootScope(rootScope)
|
||||
|
@ -77,6 +243,7 @@ void ConstraintSolver::run()
|
|||
return;
|
||||
|
||||
ToStringOptions opts;
|
||||
opts.exhaustive = true;
|
||||
|
||||
if (FFlag::DebugLuauLogSolver)
|
||||
{
|
||||
|
@ -186,6 +353,8 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
|
|||
success = tryDispatch(*bc, constraint, force);
|
||||
else if (auto nc = get<NameConstraint>(*constraint))
|
||||
success = tryDispatch(*nc, constraint);
|
||||
else if (auto taec = get<TypeAliasExpansionConstraint>(*constraint))
|
||||
success = tryDispatch(*taec, constraint);
|
||||
else
|
||||
LUAU_ASSERT(0);
|
||||
|
||||
|
@ -325,6 +494,198 @@ bool ConstraintSolver::tryDispatch(const NameConstraint& c, NotNull<const Constr
|
|||
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)
|
||||
{
|
||||
blocked[target].push_back(constraint);
|
||||
|
@ -388,7 +749,7 @@ void ConstraintSolver::unblock(TypePackId progressed)
|
|||
|
||||
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)
|
||||
|
@ -415,4 +776,12 @@ void ConstraintSolver::unify(TypePackId subPack, TypePackId superPack)
|
|||
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
|
||||
|
|
|
@ -2,45 +2,39 @@
|
|||
|
||||
#include "Luau/ConstraintSolverLogger.h"
|
||||
|
||||
#include "Luau/JsonEmitter.h"
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
static std::string dumpScopeAndChildren(const Scope2* 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(":");
|
||||
|
||||
bool comma = false;
|
||||
for (const auto& [name, type] : scope->bindings)
|
||||
Json::ObjectEmitter o = emitter.writeObject();
|
||||
|
||||
for (const auto& [name, binding] : scope->bindings)
|
||||
{
|
||||
if (comma)
|
||||
output += ",";
|
||||
|
||||
output += "\"";
|
||||
output += name.c_str();
|
||||
output += "\": \"";
|
||||
|
||||
ToStringResult result = toStringDetailed(type, opts);
|
||||
ToStringResult result = toStringDetailed(binding.typeId, opts);
|
||||
opts.nameMap = std::move(result.nameMap);
|
||||
output += result.name;
|
||||
output += "\"";
|
||||
|
||||
comma = true;
|
||||
o.writePair(name.c_str(), result.name);
|
||||
}
|
||||
|
||||
output += "},\"children\":[";
|
||||
comma = false;
|
||||
o.finish();
|
||||
emitter.writeRaw(",");
|
||||
Json::write(emitter, "children");
|
||||
emitter.writeRaw(":");
|
||||
|
||||
for (const Scope2* child : scope->children)
|
||||
Json::ArrayEmitter a = emitter.writeArray();
|
||||
for (const Scope* child : scope->children)
|
||||
{
|
||||
if (comma)
|
||||
output += ",";
|
||||
|
||||
output += dumpScopeAndChildren(child, opts);
|
||||
comma = true;
|
||||
dumpScopeAndChildren(child, emitter, opts);
|
||||
}
|
||||
|
||||
output += "]}";
|
||||
return output;
|
||||
a.finish();
|
||||
emitter.writeRaw("}");
|
||||
}
|
||||
|
||||
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 output = "[";
|
||||
bool comma = false;
|
||||
|
||||
Json::JsonEmitter emitter;
|
||||
emitter.writeRaw("[");
|
||||
for (const std::string& snapshot : snapshots)
|
||||
{
|
||||
if (comma)
|
||||
output += ",";
|
||||
output += snapshot;
|
||||
|
||||
comma = true;
|
||||
emitter.writeComma();
|
||||
emitter.writeRaw(snapshot);
|
||||
}
|
||||
|
||||
output += "]";
|
||||
return output;
|
||||
emitter.writeRaw("]");
|
||||
return emitter.str();
|
||||
}
|
||||
|
||||
void ConstraintSolverLogger::captureBoundarySnapshot(const Scope2* 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);
|
||||
snapshot += ",\"constraintGraph\":\"";
|
||||
snapshot += dumpConstraintsToDot(unsolvedConstraints, opts);
|
||||
snapshot += "\"}";
|
||||
|
||||
snapshots.push_back(std::move(snapshot));
|
||||
snapshots.push_back(emitter.str());
|
||||
}
|
||||
|
||||
void ConstraintSolverLogger::prepareStepSnapshot(
|
||||
const Scope2* 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\":";
|
||||
|
||||
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);
|
||||
preparedSnapshot = emitter.str();
|
||||
}
|
||||
|
||||
void ConstraintSolverLogger::commitPreparedStepSnapshot()
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include "Luau/BuiltinDefinitions.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAG(LuauCheckLenMT)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -123,6 +122,7 @@ declare function tonumber<T>(value: T, radix: number?): number?
|
|||
declare function rawequal<T1, T2>(a: T1, b: T2): boolean
|
||||
declare function rawget<K, V>(tab: {[K]: V}, k: K): V
|
||||
declare function rawset<K, V>(tab: {[K]: V}, k: K, v: V): {[K]: V}
|
||||
declare function rawlen<K, V>(obj: {[K]: V} | string): number
|
||||
|
||||
declare function setfenv<T..., R...>(target: number | (T...) -> R..., env: {[string]: any}): ((T...) -> R...)?
|
||||
|
||||
|
@ -206,10 +206,6 @@ std::string getBuiltinDefinitionSource()
|
|||
|
||||
std::string result = kBuiltinDefinitionLuaSrc;
|
||||
|
||||
// TODO: move this into kBuiltinDefinitionLuaSrc
|
||||
if (FFlag::LuauCheckLenMT)
|
||||
result += "declare function rawlen<K, V>(obj: {[K]: V} | string): number\n";
|
||||
|
||||
if (FFlag::LuauUnknownAndNeverType)
|
||||
result += "declare function error<T>(message: T, level: number?): never\n";
|
||||
else
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("loadDefinitionFile", "Frontend");
|
||||
|
@ -766,35 +818,28 @@ const SourceModule* Frontend::getSourceModule(const ModuleName& moduleName) cons
|
|||
return const_cast<Frontend*>(this)->getSourceModule(moduleName);
|
||||
}
|
||||
|
||||
NotNull<Scope2> Frontend::getGlobalScope2()
|
||||
NotNull<Scope> Frontend::getGlobalScope()
|
||||
{
|
||||
if (!globalScope2)
|
||||
if (!globalScope)
|
||||
{
|
||||
const SingletonTypes& singletonTypes = getSingletonTypes();
|
||||
|
||||
globalScope2 = std::make_unique<Scope2>();
|
||||
globalScope2->typeBindings["nil"] = singletonTypes.nilType;
|
||||
globalScope2->typeBindings["number"] = singletonTypes.numberType;
|
||||
globalScope2->typeBindings["string"] = singletonTypes.stringType;
|
||||
globalScope2->typeBindings["boolean"] = singletonTypes.booleanType;
|
||||
globalScope2->typeBindings["thread"] = singletonTypes.threadType;
|
||||
globalScope = typeChecker.globalScope;
|
||||
}
|
||||
|
||||
return NotNull(globalScope2.get());
|
||||
return NotNull(globalScope.get());
|
||||
}
|
||||
|
||||
ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope)
|
||||
{
|
||||
ModulePtr result = std::make_shared<Module>();
|
||||
|
||||
ConstraintGraphBuilder cgb{sourceModule.name, &result->internalTypes, NotNull(&iceHandler), getGlobalScope2()};
|
||||
ConstraintGraphBuilder cgb{sourceModule.name, &result->internalTypes, NotNull(&iceHandler), getGlobalScope()};
|
||||
cgb.visit(sourceModule.root);
|
||||
result->errors = std::move(cgb.errors);
|
||||
|
||||
ConstraintSolver cs{&result->internalTypes, NotNull(cgb.rootScope)};
|
||||
cs.run();
|
||||
|
||||
result->scope2s = std::move(cgb.scopes);
|
||||
result->scopes = std::move(cgb.scopes);
|
||||
result->astTypes = std::move(cgb.astTypes);
|
||||
result->astTypePacks = std::move(cgb.astTypePacks);
|
||||
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "Luau/TxnLog.h"
|
||||
#include "Luau/TypeArena.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauClassTypeVarsInSubstitution)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -31,6 +33,8 @@ bool Instantiation::ignoreChildren(TypeId ty)
|
|||
{
|
||||
if (log->getMutable<FunctionTypeVar>(ty))
|
||||
return true;
|
||||
else if (FFlag::LuauClassTypeVarsInSubstitution && get<ClassTypeVar>(ty))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
|
220
Analysis/src/JsonEmitter.cpp
Normal file
220
Analysis/src/JsonEmitter.cpp
Normal 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
|
|
@ -48,6 +48,7 @@ static const char* kWarningNames[] = {
|
|||
"DuplicateCondition",
|
||||
"MisleadingAndOr",
|
||||
"CommentDirective",
|
||||
"IntegerParsing",
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
@ -1451,7 +1452,7 @@ private:
|
|||
const char* checkStringFormat(const char* data, size_t size)
|
||||
{
|
||||
const char* flags = "-+ #0";
|
||||
const char* options = "cdiouxXeEfgGqs";
|
||||
const char* options = "cdiouxXeEfgGqs*";
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
|
@ -2607,6 +2608,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)
|
||||
{
|
||||
ScopePtr current = env;
|
||||
|
@ -2828,6 +2868,9 @@ std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const Sc
|
|||
if (context.warningEnabled(LintWarning::Code_CommentDirective))
|
||||
lintComments(context, hotcomments);
|
||||
|
||||
if (context.warningEnabled(LintWarning::Code_IntegerParsing))
|
||||
LintIntegerParsing::process(context);
|
||||
|
||||
std::sort(context.result.begin(), context.result.end(), WarningComparator());
|
||||
|
||||
return context.result;
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
|
||||
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative);
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAGVARIABLE(LuauForceExportSurfacesToBeNormal, false);
|
||||
|
||||
|
@ -100,29 +99,20 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
|
|||
|
||||
CloneState cloneState;
|
||||
|
||||
ScopePtr moduleScope = FFlag::DebugLuauDeferredConstraintResolution ? nullptr : getModuleScope();
|
||||
Scope2* moduleScope2 = FFlag::DebugLuauDeferredConstraintResolution ? getModuleScope2() : nullptr;
|
||||
ScopePtr moduleScope = getModuleScope();
|
||||
|
||||
TypePackId returnType = FFlag::DebugLuauDeferredConstraintResolution ? moduleScope2->returnType : moduleScope->returnType;
|
||||
TypePackId returnType = moduleScope->returnType;
|
||||
std::optional<TypePackId> varargPack = FFlag::DebugLuauDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack;
|
||||
std::unordered_map<Name, TypeFun>* exportedTypeBindings =
|
||||
FFlag::DebugLuauDeferredConstraintResolution ? nullptr : &moduleScope->exportedTypeBindings;
|
||||
|
||||
returnType = clone(returnType, interfaceTypes, cloneState);
|
||||
|
||||
if (moduleScope)
|
||||
moduleScope->returnType = returnType;
|
||||
if (varargPack)
|
||||
{
|
||||
moduleScope->returnType = returnType;
|
||||
if (varargPack)
|
||||
{
|
||||
varargPack = clone(*varargPack, interfaceTypes, cloneState);
|
||||
moduleScope->varargPack = varargPack;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(moduleScope2);
|
||||
moduleScope2->returnType = returnType; // TODO varargPack
|
||||
varargPack = clone(*varargPack, interfaceTypes, cloneState);
|
||||
moduleScope->varargPack = varargPack;
|
||||
}
|
||||
|
||||
ForceNormal forceNormal{&interfaceTypes};
|
||||
|
@ -149,20 +139,17 @@ void Module::clonePublicInterface(InternalErrorReporter& 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
|
||||
// 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);
|
||||
forceNormal.traverse(param.ty);
|
||||
|
||||
if (param.defaultValue)
|
||||
{
|
||||
normalize(*param.defaultValue, interfaceTypes, ice);
|
||||
forceNormal.traverse(*param.defaultValue);
|
||||
}
|
||||
if (param.defaultValue)
|
||||
{
|
||||
normalize(*param.defaultValue, interfaceTypes, ice);
|
||||
forceNormal.traverse(*param.defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,10 +188,4 @@ ScopePtr Module::getModuleScope() const
|
|||
return scopes.front().second;
|
||||
}
|
||||
|
||||
Scope2* Module::getModuleScope2() const
|
||||
{
|
||||
LUAU_ASSERT(!scope2s.empty());
|
||||
return scope2s.front().second.get();
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -13,7 +13,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauCopyBeforeNormalizing, false)
|
|||
// This could theoretically be 2000 on amd64, but x86 requires this.
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200);
|
||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeCombineTableFix, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauNormalizeFlagIsConservative, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixNormalizationOfCyclicUnions, false);
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAG(LuauQuantifyConstrained)
|
||||
|
@ -89,13 +88,7 @@ static bool areNormal_(const T& t, const std::unordered_set<void*>& seen, Intern
|
|||
if (count >= FInt::LuauNormalizeIterationLimit)
|
||||
ice.ice("Luau::areNormal hit iteration limit");
|
||||
|
||||
if (FFlag::LuauNormalizeFlagIsConservative)
|
||||
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 ty->normal;
|
||||
};
|
||||
|
||||
return std::all_of(begin(t), end(t), isNormal);
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "Luau/TxnLog.h"
|
||||
#include "Luau/VisitTypeVar.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauAlwaysQuantify);
|
||||
LUAU_FASTFLAG(DebugLuauSharedSelf)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAGVARIABLE(LuauQuantifyConstrained, false)
|
||||
|
@ -16,13 +15,13 @@ namespace Luau
|
|||
{
|
||||
|
||||
/// @return true if outer encloses inner
|
||||
static bool subsumes(Scope2* outer, Scope2* inner)
|
||||
static bool subsumes(Scope* outer, Scope* inner)
|
||||
{
|
||||
while (inner)
|
||||
{
|
||||
if (inner == outer)
|
||||
return true;
|
||||
inner = inner->parent;
|
||||
inner = inner->parent.get();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -33,7 +32,7 @@ struct Quantifier final : TypeVarOnceVisitor
|
|||
TypeLevel level;
|
||||
std::vector<TypeId> generics;
|
||||
std::vector<TypePackId> genericPacks;
|
||||
Scope2* scope = nullptr;
|
||||
Scope* scope = nullptr;
|
||||
bool seenGenericType = false;
|
||||
bool seenMutableType = false;
|
||||
|
||||
|
@ -43,20 +42,20 @@ struct Quantifier final : TypeVarOnceVisitor
|
|||
LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution);
|
||||
}
|
||||
|
||||
explicit Quantifier(Scope2* scope)
|
||||
explicit Quantifier(Scope* scope)
|
||||
: scope(scope)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution);
|
||||
}
|
||||
|
||||
/// @return true if outer encloses inner
|
||||
bool subsumes(Scope2* outer, Scope2* inner)
|
||||
bool subsumes(Scope* outer, Scope* inner)
|
||||
{
|
||||
while (inner)
|
||||
{
|
||||
if (inner == outer)
|
||||
return true;
|
||||
inner = inner->parent;
|
||||
inner = inner->parent.get();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -203,36 +202,20 @@ void quantify(TypeId ty, TypeLevel level)
|
|||
|
||||
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty);
|
||||
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());
|
||||
}
|
||||
else
|
||||
{
|
||||
ftv->generics = q.generics;
|
||||
ftv->genericPacks = q.genericPacks;
|
||||
}
|
||||
ftv->generics.insert(ftv->generics.end(), q.generics.begin(), q.generics.end());
|
||||
ftv->genericPacks.insert(ftv->genericPacks.end(), q.genericPacks.begin(), q.genericPacks.end());
|
||||
}
|
||||
}
|
||||
|
||||
void quantify(TypeId ty, Scope2* scope)
|
||||
void quantify(TypeId ty, Scope* scope)
|
||||
{
|
||||
Quantifier q{scope};
|
||||
q.traverse(ty);
|
||||
|
||||
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty);
|
||||
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());
|
||||
}
|
||||
else
|
||||
{
|
||||
ftv->generics = q.generics;
|
||||
ftv->genericPacks = q.genericPacks;
|
||||
}
|
||||
ftv->generics.insert(ftv->generics.end(), q.generics.begin(), q.generics.end());
|
||||
ftv->genericPacks.insert(ftv->genericPacks.end(), q.genericPacks.begin(), q.genericPacks.end());
|
||||
|
||||
if (ftv->generics.empty() && ftv->genericPacks.empty() && !q.seenMutableType && !q.seenGenericType)
|
||||
ftv->hasNoGenerics = true;
|
||||
|
@ -240,11 +223,11 @@ void quantify(TypeId ty, Scope2* scope)
|
|||
|
||||
struct PureQuantifier : Substitution
|
||||
{
|
||||
Scope2* scope;
|
||||
Scope* scope;
|
||||
std::vector<TypeId> insertedGenerics;
|
||||
std::vector<TypePackId> insertedGenericPacks;
|
||||
|
||||
PureQuantifier(TypeArena* arena, Scope2* scope)
|
||||
PureQuantifier(TypeArena* arena, Scope* scope)
|
||||
: Substitution(TxnLog::empty(), arena)
|
||||
, scope(scope)
|
||||
{
|
||||
|
@ -322,7 +305,7 @@ struct PureQuantifier : Substitution
|
|||
}
|
||||
};
|
||||
|
||||
TypeId quantify(TypeArena* arena, TypeId ty, Scope2* scope)
|
||||
TypeId quantify(TypeArena* arena, TypeId ty, Scope* scope)
|
||||
{
|
||||
PureQuantifier quantifier{arena, scope};
|
||||
std::optional<TypeId> result = quantifier.substitute(ty);
|
||||
|
|
|
@ -21,22 +21,6 @@ Scope::Scope(const ScopePtr& parent, int subLevel)
|
|||
level.subLevel = subLevel;
|
||||
}
|
||||
|
||||
std::optional<TypeId> Scope::lookup(const Symbol& name)
|
||||
{
|
||||
Scope* scope = this;
|
||||
|
||||
while (scope)
|
||||
{
|
||||
auto it = scope->bindings.find(name);
|
||||
if (it != scope->bindings.end())
|
||||
return it->second.typeId;
|
||||
|
||||
scope = scope->parent.get();
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TypeFun> Scope::lookupType(const Name& name)
|
||||
{
|
||||
const Scope* scope = this;
|
||||
|
@ -121,48 +105,48 @@ std::optional<Binding> Scope::linearSearchForBinding(const std::string& name, bo
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TypeId> Scope2::lookup(Symbol sym)
|
||||
std::optional<TypeId> Scope::lookup(Symbol sym)
|
||||
{
|
||||
Scope2* s = this;
|
||||
Scope* s = this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto it = s->bindings.find(sym);
|
||||
if (it != s->bindings.end())
|
||||
return it->second;
|
||||
return it->second.typeId;
|
||||
|
||||
if (s->parent)
|
||||
s = s->parent;
|
||||
s = s->parent.get();
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<TypeId> Scope2::lookupTypeBinding(const Name& name)
|
||||
std::optional<TypeFun> Scope::lookupTypeBinding(const Name& name)
|
||||
{
|
||||
Scope2* s = this;
|
||||
Scope* s = this;
|
||||
while (s)
|
||||
{
|
||||
auto it = s->typeBindings.find(name);
|
||||
if (it != s->typeBindings.end())
|
||||
return it->second;
|
||||
|
||||
s = s->parent;
|
||||
s = s->parent.get();
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TypePackId> Scope2::lookupTypePackBinding(const Name& name)
|
||||
std::optional<TypePackId> Scope::lookupTypePackBinding(const Name& name)
|
||||
{
|
||||
Scope2* s = this;
|
||||
Scope* s = this;
|
||||
while (s)
|
||||
{
|
||||
auto it = s->typePackBindings.find(name);
|
||||
if (it != s->typePackBindings.end())
|
||||
return it->second;
|
||||
|
||||
s = s->parent;
|
||||
s = s->parent.get();
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
|
|
|
@ -9,9 +9,12 @@
|
|||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauAnyificationMustClone, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubstitutionFixMissingFields, false)
|
||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
||||
LUAU_FASTFLAGVARIABLE(LuauClassTypeVarsInSubstitution, false)
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubstitutionReentrant, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -28,6 +31,14 @@ void Tarjan::visitChildren(TypeId ty, int index)
|
|||
|
||||
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->retTypes);
|
||||
}
|
||||
|
@ -68,6 +79,25 @@ void Tarjan::visitChildren(TypeId ty, int index)
|
|||
for (TypeId part : ctv->parts)
|
||||
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)
|
||||
|
@ -267,6 +297,24 @@ TarjanResult Tarjan::visitRoot(TypePackId tp)
|
|||
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)
|
||||
{
|
||||
if (dirty.size() <= size_t(index))
|
||||
|
@ -330,16 +378,46 @@ std::optional<TypeId> Substitution::substitute(TypeId ty)
|
|||
{
|
||||
ty = log->follow(ty);
|
||||
|
||||
// clear algorithm state for reentrancy
|
||||
if (FFlag::LuauSubstitutionReentrant)
|
||||
clearTarjan();
|
||||
|
||||
auto result = findDirty(ty);
|
||||
if (result != TarjanResult::Ok)
|
||||
return std::nullopt;
|
||||
|
||||
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)
|
||||
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);
|
||||
return newTy;
|
||||
}
|
||||
|
@ -348,16 +426,46 @@ std::optional<TypePackId> Substitution::substitute(TypePackId tp)
|
|||
{
|
||||
tp = log->follow(tp);
|
||||
|
||||
// clear algorithm state for reentrancy
|
||||
if (FFlag::LuauSubstitutionReentrant)
|
||||
clearTarjan();
|
||||
|
||||
auto result = findDirty(tp);
|
||||
if (result != TarjanResult::Ok)
|
||||
return std::nullopt;
|
||||
|
||||
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)
|
||||
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);
|
||||
return newTp;
|
||||
}
|
||||
|
@ -385,6 +493,8 @@ TypePackId Substitution::clone(TypePackId tp)
|
|||
{
|
||||
VariadicTypePack clone;
|
||||
clone.ty = vtp->ty;
|
||||
if (FFlag::LuauSubstitutionFixMissingFields)
|
||||
clone.hidden = vtp->hidden;
|
||||
return addTypePack(std::move(clone));
|
||||
}
|
||||
else
|
||||
|
@ -395,6 +505,9 @@ void Substitution::foundDirty(TypeId ty)
|
|||
{
|
||||
ty = log->follow(ty);
|
||||
|
||||
if (FFlag::LuauSubstitutionReentrant && newTypes.contains(ty))
|
||||
return;
|
||||
|
||||
if (isDirty(ty))
|
||||
newTypes[ty] = follow(clean(ty));
|
||||
else
|
||||
|
@ -405,6 +518,9 @@ void Substitution::foundDirty(TypePackId tp)
|
|||
{
|
||||
tp = log->follow(tp);
|
||||
|
||||
if (FFlag::LuauSubstitutionReentrant && newPacks.contains(tp))
|
||||
return;
|
||||
|
||||
if (isDirty(tp))
|
||||
newPacks[tp] = follow(clean(tp));
|
||||
else
|
||||
|
@ -446,6 +562,14 @@ void Substitution::replaceChildren(TypeId 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->retTypes = replace(ftv->retTypes);
|
||||
}
|
||||
|
@ -486,6 +610,25 @@ void Substitution::replaceChildren(TypeId ty)
|
|||
for (TypeId& part : ctv->parts)
|
||||
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)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
|
||||
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSpecialTypesAsterisked, false)
|
||||
|
||||
/*
|
||||
* Prefix generic typenames with gen-
|
||||
|
@ -231,6 +232,11 @@ struct StringifierState
|
|||
emit(std::to_string(i).c_str());
|
||||
}
|
||||
|
||||
void emit(size_t i)
|
||||
{
|
||||
emit(std::to_string(i).c_str());
|
||||
}
|
||||
|
||||
void indent()
|
||||
{
|
||||
indentation += 4;
|
||||
|
@ -277,7 +283,10 @@ struct TypeVarStringifier
|
|||
if (tv->ty.valueless_by_exception())
|
||||
{
|
||||
state.result.error = true;
|
||||
state.emit("< VALUELESS BY EXCEPTION >");
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit("* VALUELESS BY EXCEPTION *");
|
||||
else
|
||||
state.emit("< VALUELESS BY EXCEPTION >");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -406,6 +415,13 @@ struct TypeVarStringifier
|
|||
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)
|
||||
{
|
||||
switch (ptv.type)
|
||||
|
@ -453,7 +469,10 @@ struct TypeVarStringifier
|
|||
if (state.hasSeen(&ftv))
|
||||
{
|
||||
state.result.cycle = true;
|
||||
state.emit("<CYCLE>");
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit("*CYCLE*");
|
||||
else
|
||||
state.emit("<CYCLE>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -561,7 +580,10 @@ struct TypeVarStringifier
|
|||
if (state.hasSeen(&ttv))
|
||||
{
|
||||
state.result.cycle = true;
|
||||
state.emit("<CYCLE>");
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit("*CYCLE*");
|
||||
else
|
||||
state.emit("<CYCLE>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -691,7 +713,10 @@ struct TypeVarStringifier
|
|||
if (state.hasSeen(&uv))
|
||||
{
|
||||
state.result.cycle = true;
|
||||
state.emit("<CYCLE>");
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit("*CYCLE*");
|
||||
else
|
||||
state.emit("<CYCLE>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -758,7 +783,10 @@ struct TypeVarStringifier
|
|||
if (state.hasSeen(&uv))
|
||||
{
|
||||
state.result.cycle = true;
|
||||
state.emit("<CYCLE>");
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit("*CYCLE*");
|
||||
else
|
||||
state.emit("<CYCLE>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -803,7 +831,10 @@ struct TypeVarStringifier
|
|||
void operator()(TypeId, const ErrorTypeVar& tv)
|
||||
{
|
||||
state.result.error = true;
|
||||
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
|
||||
else
|
||||
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
|
||||
}
|
||||
|
||||
void operator()(TypeId, const LazyTypeVar& ltv)
|
||||
|
@ -857,7 +888,10 @@ struct TypePackStringifier
|
|||
if (tp->ty.valueless_by_exception())
|
||||
{
|
||||
state.result.error = true;
|
||||
state.emit("< VALUELESS TP BY EXCEPTION >");
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit("* VALUELESS TP BY EXCEPTION *");
|
||||
else
|
||||
state.emit("< VALUELESS TP BY EXCEPTION >");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -881,7 +915,10 @@ struct TypePackStringifier
|
|||
if (state.hasSeen(&tp))
|
||||
{
|
||||
state.result.cycle = true;
|
||||
state.emit("<CYCLETP>");
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit("*CYCLETP*");
|
||||
else
|
||||
state.emit("<CYCLETP>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -926,14 +963,22 @@ struct TypePackStringifier
|
|||
void operator()(TypePackId, const Unifiable::Error& error)
|
||||
{
|
||||
state.result.error = true;
|
||||
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
|
||||
else
|
||||
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
|
||||
}
|
||||
|
||||
void operator()(TypePackId, const VariadicTypePack& pack)
|
||||
{
|
||||
state.emit("...");
|
||||
if (FFlag::DebugLuauVerboseTypeNames && pack.hidden)
|
||||
state.emit("<hidden>");
|
||||
{
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
state.emit("*hidden*");
|
||||
else
|
||||
state.emit("<hidden>");
|
||||
}
|
||||
stringify(pack.ty);
|
||||
}
|
||||
|
||||
|
@ -1128,7 +1173,11 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
|
|||
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
|
||||
{
|
||||
result.truncated = true;
|
||||
result.name += "... <TRUNCATED>";
|
||||
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
result.name += "... *TRUNCATED*";
|
||||
else
|
||||
result.name += "... <TRUNCATED>";
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -1199,7 +1248,12 @@ ToStringResult toStringDetailed(TypePackId tp, const ToStringOptions& opts)
|
|||
}
|
||||
|
||||
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
|
||||
result.name += "... <TRUNCATED>";
|
||||
{
|
||||
if (FFlag::LuauSpecialTypesAsterisked)
|
||||
result.name += "... *TRUNCATED*";
|
||||
else
|
||||
result.name += "... <TRUNCATED>";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1417,6 +1471,12 @@ std::string toString(const Constraint& constraint, ToStringOptions& opts)
|
|||
opts.nameMap = std::move(namedStr.nameMap);
|
||||
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
|
||||
static_assert(always_false_v<T>, "Non-exhaustive constraint switch");
|
||||
};
|
||||
|
|
|
@ -99,6 +99,11 @@ public:
|
|||
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)
|
||||
{
|
||||
AstArray<AstType*> types;
|
||||
|
|
|
@ -67,8 +67,18 @@ struct TypeChecker2 : public AstVisitor
|
|||
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)
|
||||
{
|
||||
if (exprs.size == 0)
|
||||
return arena.addTypePack(TypePack{{}, std::nullopt});
|
||||
|
||||
std::vector<TypeId> head;
|
||||
|
||||
for (size_t i = 0; i < exprs.size - 1; ++i)
|
||||
|
@ -80,14 +90,14 @@ struct TypeChecker2 : public AstVisitor
|
|||
return arena.addTypePack(TypePack{head, tail});
|
||||
}
|
||||
|
||||
Scope2* findInnermostScope(Location location)
|
||||
Scope* findInnermostScope(Location location)
|
||||
{
|
||||
Scope2* bestScope = module->getModuleScope2();
|
||||
Location bestLocation = module->scope2s[0].first;
|
||||
Scope* bestScope = module->getModuleScope().get();
|
||||
Location bestLocation = module->scopes[0].first;
|
||||
|
||||
for (size_t i = 0; i < module->scope2s.size(); ++i)
|
||||
for (size_t i = 0; i < module->scopes.size(); ++i)
|
||||
{
|
||||
auto& [scopeBounds, scope] = module->scope2s[i];
|
||||
auto& [scopeBounds, scope] = module->scopes[i];
|
||||
if (scopeBounds.encloses(location))
|
||||
{
|
||||
if (scopeBounds.begin > bestLocation.begin || scopeBounds.end < bestLocation.end)
|
||||
|
@ -181,7 +191,7 @@ struct TypeChecker2 : public AstVisitor
|
|||
|
||||
bool visit(AstStatReturn* ret) override
|
||||
{
|
||||
Scope2* scope = findInnermostScope(ret->location);
|
||||
Scope* scope = findInnermostScope(ret->location);
|
||||
TypePackId expectedRetType = scope->returnType;
|
||||
|
||||
TypeArena arena;
|
||||
|
@ -359,13 +369,154 @@ struct TypeChecker2 : public AstVisitor
|
|||
|
||||
bool visit(AstTypeReference* ty) override
|
||||
{
|
||||
Scope2* scope = findInnermostScope(ty->location);
|
||||
Scope* scope = findInnermostScope(ty->location);
|
||||
LUAU_ASSERT(scope);
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// 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/ApplyTypeFunction.h"
|
||||
#include "Luau/Clone.h"
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/Instantiation.h"
|
||||
|
@ -35,17 +36,13 @@ LUAU_FASTFLAGVARIABLE(LuauExpectedTableUnionIndexerType, false)
|
|||
LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix2, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix3, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
|
||||
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysQuantify, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false)
|
||||
LUAU_FASTFLAG(LuauQuantifyConstrained)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFalsyPredicateReturnsNilInstead, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCheckLenMT, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCheckGenericHOFTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false)
|
||||
|
@ -1667,7 +1664,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar
|
|||
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
||||
ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
|
||||
|
||||
if (FFlag::LuauSelfCallAutocompleteFix2)
|
||||
if (FFlag::LuauSelfCallAutocompleteFix3)
|
||||
ftv->hasSelf = true;
|
||||
}
|
||||
}
|
||||
|
@ -2111,35 +2108,16 @@ std::vector<TypeId> TypeChecker::reduceUnion(const std::vector<TypeId>& types)
|
|||
|
||||
if (const UnionTypeVar* utv = get<UnionTypeVar>(t))
|
||||
{
|
||||
if (FFlag::LuauReduceUnionRecursion)
|
||||
for (TypeId ty : utv)
|
||||
{
|
||||
for (TypeId ty : utv)
|
||||
{
|
||||
if (FFlag::LuauNormalizeFlagIsConservative)
|
||||
ty = follow(ty);
|
||||
if (get<NeverTypeVar>(ty))
|
||||
continue;
|
||||
if (get<ErrorTypeVar>(ty) || get<AnyTypeVar>(ty))
|
||||
return {ty};
|
||||
ty = follow(ty);
|
||||
if (get<NeverTypeVar>(ty))
|
||||
continue;
|
||||
if (get<ErrorTypeVar>(ty) || get<AnyTypeVar>(ty))
|
||||
return {ty};
|
||||
|
||||
if (result.end() == std::find(result.begin(), result.end(), 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);
|
||||
}
|
||||
if (result.end() == std::find(result.begin(), result.end(), ty))
|
||||
result.push_back(ty);
|
||||
}
|
||||
}
|
||||
else if (std::find(result.begin(), result.end(), t) == result.end())
|
||||
|
@ -2467,7 +2445,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
|
|||
|
||||
DenseHashSet<TypeId> seen{nullptr};
|
||||
|
||||
if (FFlag::LuauCheckLenMT && typeCouldHaveMetatable(operandType))
|
||||
if (typeCouldHaveMetatable(operandType))
|
||||
{
|
||||
if (auto fnt = findMetatableEntry(operandType, "__len", expr.location, /* addErrors= */ true))
|
||||
{
|
||||
|
@ -4781,16 +4759,8 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
|
|||
{
|
||||
const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty);
|
||||
|
||||
if (FFlag::LuauAlwaysQuantify)
|
||||
{
|
||||
if (ftv)
|
||||
Luau::quantify(ty, scope->level);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ftv && ftv->generics.empty() && ftv->genericPacks.empty())
|
||||
Luau::quantify(ty, scope->level);
|
||||
}
|
||||
if (ftv)
|
||||
Luau::quantify(ty, scope->level);
|
||||
|
||||
if (FFlag::LuauLowerBoundsCalculation && ftv)
|
||||
{
|
||||
|
@ -5264,7 +5234,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
|
|||
if (notEnoughParameters && hasDefaultParameters)
|
||||
{
|
||||
// 'applyTypeFunction' is used to substitute default types that reference previous generic types
|
||||
ApplyTypeFunction applyTypeFunction{¤tModule->internalTypes, scope->level};
|
||||
ApplyTypeFunction applyTypeFunction{¤tModule->internalTypes};
|
||||
|
||||
for (size_t i = 0; i < typesProvided; ++i)
|
||||
applyTypeFunction.typeArguments[tf->typeParams[i].ty] = typeParams[i];
|
||||
|
@ -5505,65 +5475,13 @@ TypePackId TypeChecker::resolveTypePack(const ScopePtr& scope, const AstTypePack
|
|||
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,
|
||||
const std::vector<TypePackId>& typePackParams, const Location& location)
|
||||
{
|
||||
if (tf.typeParams.empty() && tf.typePackParams.empty())
|
||||
return tf.type;
|
||||
|
||||
ApplyTypeFunction applyTypeFunction{¤tModule->internalTypes, scope->level};
|
||||
ApplyTypeFunction applyTypeFunction{¤tModule->internalTypes};
|
||||
|
||||
for (size_t i = 0; i < tf.typeParams.size(); ++i)
|
||||
applyTypeFunction.typeArguments[tf.typeParams[i].ty] = typeParams[i];
|
||||
|
|
|
@ -445,6 +445,16 @@ BlockedTypeVar::BlockedTypeVar()
|
|||
|
||||
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)
|
||||
: argTypes(argTypes)
|
||||
, retTypes(retTypes)
|
||||
|
@ -1058,7 +1068,7 @@ ConstrainedTypeVarIterator end(const ConstrainedTypeVar* ctv)
|
|||
|
||||
static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const char* data, size_t size)
|
||||
{
|
||||
const char* options = "cdiouxXeEfgGqs";
|
||||
const char* options = "cdiouxXeEfgGqs*";
|
||||
|
||||
std::vector<TypeId> result;
|
||||
|
||||
|
@ -1072,7 +1082,7 @@ static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const cha
|
|||
continue;
|
||||
|
||||
// we just ignore all characters (including flags/precision) up until first alphabetic character
|
||||
while (i < size && !(data[i] > 0 && isalpha(data[i])))
|
||||
while (i < size && !(data[i] > 0 && (isalpha(data[i]) || data[i] == '*')))
|
||||
i++;
|
||||
|
||||
if (i == size)
|
||||
|
@ -1080,6 +1090,8 @@ static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const cha
|
|||
|
||||
if (data[i] == 'q' || data[i] == 's')
|
||||
result.push_back(typechecker.stringType);
|
||||
else if (data[i] == '*')
|
||||
result.push_back(typechecker.unknownType);
|
||||
else if (strchr(options, data[i]))
|
||||
result.push_back(typechecker.numberType);
|
||||
else
|
||||
|
@ -1410,4 +1422,19 @@ bool hasTag(const Property& prop, const std::string& 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
|
||||
|
|
|
@ -12,7 +12,7 @@ Free::Free(TypeLevel level)
|
|||
{
|
||||
}
|
||||
|
||||
Free::Free(Scope2* scope)
|
||||
Free::Free(Scope* scope)
|
||||
: scope(scope)
|
||||
{
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ Generic::Generic(const Name& name)
|
|||
{
|
||||
}
|
||||
|
||||
Generic::Generic(Scope2* scope)
|
||||
Generic::Generic(Scope* scope)
|
||||
: index(++nextIndex)
|
||||
, scope(scope)
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ Generic::Generic(TypeLevel level, const Name& name)
|
|||
{
|
||||
}
|
||||
|
||||
Generic::Generic(Scope2* scope, const Name& name)
|
||||
Generic::Generic(Scope* scope, const Name& name)
|
||||
: index(++nextIndex)
|
||||
, scope(scope)
|
||||
, name(name)
|
||||
|
|
|
@ -478,16 +478,26 @@ public:
|
|||
bool value;
|
||||
};
|
||||
|
||||
enum class ConstantNumberParseResult
|
||||
{
|
||||
Ok,
|
||||
Malformed,
|
||||
BinOverflow,
|
||||
HexOverflow,
|
||||
DoublePrefix,
|
||||
};
|
||||
|
||||
class AstExprConstantNumber : public AstExpr
|
||||
{
|
||||
public:
|
||||
LUAU_RTTI(AstExprConstantNumber)
|
||||
|
||||
AstExprConstantNumber(const Location& location, double value);
|
||||
AstExprConstantNumber(const Location& location, double value, ConstantNumberParseResult parseResult = ConstantNumberParseResult::Ok);
|
||||
|
||||
void visit(AstVisitor* visitor) override;
|
||||
|
||||
double value;
|
||||
ConstantNumberParseResult parseResult;
|
||||
};
|
||||
|
||||
class AstExprConstantString : public AstExpr
|
||||
|
|
|
@ -50,9 +50,10 @@ void AstExprConstantBool::visit(AstVisitor* visitor)
|
|||
visitor->visit(this);
|
||||
}
|
||||
|
||||
AstExprConstantNumber::AstExprConstantNumber(const Location& location, double value)
|
||||
AstExprConstantNumber::AstExprConstantNumber(const Location& location, double value, ConstantNumberParseResult parseResult)
|
||||
: AstExpr(ClassIndex(), location)
|
||||
, value(value)
|
||||
, parseResult(parseResult)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,10 @@ LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseWrongNamedType, 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_FASTFLAGVARIABLE(LuauAlwaysCaptureHotComments, false)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauInterpolatedStringBaseSupport, false)
|
||||
|
||||
bool lua_telemetry_parsed_out_of_range_bin_integer = false;
|
||||
|
@ -2050,8 +2049,10 @@ AstExpr* Parser::parseAssertionExpr()
|
|||
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;
|
||||
unsigned long long value = strtoull(data, &end, base);
|
||||
|
||||
|
@ -2071,9 +2072,6 @@ static const char* parseInteger(double& result, const char* data, int base)
|
|||
else
|
||||
lua_telemetry_parsed_out_of_range_hex_integer = true;
|
||||
}
|
||||
|
||||
if (FFlag::LuauErrorParseIntegerIssues)
|
||||
return "Integer number value is out of range";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2081,11 +2079,13 @@ static const char* parseInteger(double& result, const char* data, int base)
|
|||
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
|
||||
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
|
||||
if (data[0] == '0' && (data[1] == 'x' || data[1] == 'X') && data[2])
|
||||
|
@ -2093,10 +2093,7 @@ static const char* parseNumber(double& result, const char* data)
|
|||
if (DFFlag::LuaReportParseIntegerIssues && data[2] == '0' && (data[3] == 'x' || data[3] == 'X'))
|
||||
lua_telemetry_parsed_double_prefix_hex_integer = true;
|
||||
|
||||
if (FFlag::LuauErrorParseIntegerIssues)
|
||||
return parseInteger(result, data, 16); // keep prefix, it's handled by 'strtoull'
|
||||
else
|
||||
return parseInteger(result, data + 2, 16);
|
||||
return parseInteger_DEPRECATED(result, data + 2, 16);
|
||||
}
|
||||
|
||||
char* end = nullptr;
|
||||
|
@ -2108,6 +2105,8 @@ static const char* parseNumber(double& result, const char* data)
|
|||
|
||||
static bool parseNumber_DEPRECATED(double& result, const char* data)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauLintParseIntegerIssues);
|
||||
|
||||
// binary literal
|
||||
if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2])
|
||||
{
|
||||
|
@ -2136,6 +2135,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
|
||||
AstExpr* Parser::parseSimpleExpr()
|
||||
{
|
||||
|
@ -2176,10 +2242,21 @@ AstExpr* Parser::parseSimpleExpr()
|
|||
scratchData.erase(std::remove(scratchData.begin(), scratchData.end(), '_'), scratchData.end());
|
||||
}
|
||||
|
||||
if (DFFlag::LuaReportParseIntegerIssues || FFlag::LuauErrorParseIntegerIssues)
|
||||
if (FFlag::LuauLintParseIntegerIssues)
|
||||
{
|
||||
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();
|
||||
|
||||
|
@ -3014,39 +3091,34 @@ AstTypeError* Parser::reportTypeAnnotationError(const Location& location, const
|
|||
|
||||
void Parser::nextLexeme()
|
||||
{
|
||||
if (options.captureComments || FFlag::LuauAlwaysCaptureHotComments)
|
||||
Lexeme::Type type = lexer.next(/* skipComments= */ false, true).type;
|
||||
|
||||
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
|
||||
{
|
||||
Lexeme::Type type = lexer.next(/* skipComments= */ false, true).type;
|
||||
const Lexeme& lexeme = lexer.current();
|
||||
|
||||
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
|
||||
if (options.captureComments)
|
||||
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
||||
|
||||
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
||||
// The parser will turn this into a proper syntax error.
|
||||
if (lexeme.type == Lexeme::BrokenComment)
|
||||
return;
|
||||
|
||||
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting / compiling
|
||||
if (lexeme.type == Lexeme::Comment && lexeme.length && lexeme.data[0] == '!')
|
||||
{
|
||||
const Lexeme& lexeme = lexer.current();
|
||||
const char* text = lexeme.data;
|
||||
|
||||
if (options.captureComments)
|
||||
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
||||
unsigned int end = lexeme.length;
|
||||
while (end > 0 && isSpace(text[end - 1]))
|
||||
--end;
|
||||
|
||||
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
||||
// The parser will turn this into a proper syntax error.
|
||||
if (lexeme.type == Lexeme::BrokenComment)
|
||||
return;
|
||||
|
||||
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting / compiling
|
||||
if (lexeme.type == Lexeme::Comment && lexeme.length && lexeme.data[0] == '!')
|
||||
{
|
||||
const char* text = lexeme.data;
|
||||
|
||||
unsigned int end = lexeme.length;
|
||||
while (end > 0 && isSpace(text[end - 1]))
|
||||
--end;
|
||||
|
||||
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
|
||||
}
|
||||
|
||||
type = lexer.next(/* skipComments= */ false, /* updatePrevLocation= */ false).type;
|
||||
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
|
||||
}
|
||||
|
||||
type = lexer.next(/* skipComments= */ false, /* updatePrevLocation= */ false).type;
|
||||
}
|
||||
else
|
||||
lexer.next();
|
||||
}
|
||||
|
||||
} // namespace Luau
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "Luau/Common.h"
|
||||
#include "Luau/Ast.h"
|
||||
#include "Luau/JsonEncoder.h"
|
||||
#include "Luau/AstJsonEncoder.h"
|
||||
#include "Luau/Parser.h"
|
||||
#include "Luau/ParseOptions.h"
|
||||
|
||||
|
|
46
CLI/Repl.cpp
46
CLI/Repl.cpp
|
@ -20,6 +20,9 @@
|
|||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef CALLGRIND
|
||||
|
@ -27,6 +30,7 @@
|
|||
#endif
|
||||
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauTimeTracing)
|
||||
|
||||
|
@ -47,6 +51,35 @@ enum class CompileFormat
|
|||
|
||||
constexpr int MaxTraversalLimit = 50;
|
||||
|
||||
// Ctrl-C handling
|
||||
static void sigintCallback(lua_State* L, int gc)
|
||||
{
|
||||
if (gc >= 0)
|
||||
return;
|
||||
|
||||
lua_callbacks(L)->interrupt = NULL;
|
||||
|
||||
lua_rawcheckstack(L, 1); // reserve space for error string
|
||||
luaL_error(L, "Execution interrupted");
|
||||
}
|
||||
|
||||
static lua_State* replState = NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
BOOL WINAPI sigintHandler(DWORD signal)
|
||||
{
|
||||
if (signal == CTRL_C_EVENT && replState)
|
||||
lua_callbacks(replState)->interrupt = &sigintCallback;
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
static void sigintHandler(int signum)
|
||||
{
|
||||
if (signum == SIGINT && replState)
|
||||
lua_callbacks(replState)->interrupt = &sigintCallback;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct GlobalOptions
|
||||
{
|
||||
int optimizationLevel = 1;
|
||||
|
@ -76,8 +109,8 @@ static int lua_loadstring(lua_State* L)
|
|||
return 1;
|
||||
|
||||
lua_pushnil(L);
|
||||
lua_insert(L, -2); /* put before error message */
|
||||
return 2; /* return nil plus error message */
|
||||
lua_insert(L, -2); // put before error message
|
||||
return 2; // return nil plus error message
|
||||
}
|
||||
|
||||
static int finishrequire(lua_State* L)
|
||||
|
@ -535,6 +568,15 @@ static void runRepl()
|
|||
lua_State* L = globalState.get();
|
||||
|
||||
setupState(L);
|
||||
|
||||
// setup Ctrl+C handling
|
||||
replState = L;
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler(sigintHandler, TRUE);
|
||||
#else
|
||||
signal(SIGINT, sigintHandler);
|
||||
#endif
|
||||
|
||||
luaL_sandboxthread(L);
|
||||
runReplImpl(L);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Repl.h"
|
||||
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
return replMain(argc, argv);
|
||||
|
|
|
@ -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
|
||||
// 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
|
||||
enum LuauOpcode
|
||||
{
|
||||
|
@ -367,6 +375,20 @@ enum LuauOpcode
|
|||
// D: jump offset (-32768..32767)
|
||||
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!
|
||||
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
|
||||
LBC_VERSION_MIN = 2,
|
||||
LBC_VERSION_MAX = 2,
|
||||
LBC_VERSION_MAX = 3,
|
||||
LBC_VERSION_TARGET = 2,
|
||||
// Types of constant table entries
|
||||
LBC_CONSTANT_NIL = 0,
|
||||
|
|
|
@ -20,12 +20,6 @@
|
|||
#define LUAU_DEBUGBREAK() __builtin_trap()
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -67,16 +61,13 @@ struct FValue
|
|||
const char* name;
|
||||
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)
|
||||
, dynamic(dynamic)
|
||||
, name(name)
|
||||
, next(list)
|
||||
{
|
||||
list = this;
|
||||
|
||||
if (reg)
|
||||
reg(name, &value, dynamic);
|
||||
}
|
||||
|
||||
operator T() const
|
||||
|
@ -98,7 +89,7 @@ FValue<T>* FValue<T>::list = nullptr;
|
|||
#define LUAU_FASTFLAGVARIABLE(flag, def) \
|
||||
namespace FFlag \
|
||||
{ \
|
||||
Luau::FValue<bool> flag(#flag, def, false, nullptr); \
|
||||
Luau::FValue<bool> flag(#flag, def, false); \
|
||||
}
|
||||
#define LUAU_FASTINT(flag) \
|
||||
namespace FInt \
|
||||
|
@ -108,7 +99,7 @@ FValue<T>* FValue<T>::list = nullptr;
|
|||
#define LUAU_FASTINTVARIABLE(flag, def) \
|
||||
namespace FInt \
|
||||
{ \
|
||||
Luau::FValue<int> flag(#flag, def, false, nullptr); \
|
||||
Luau::FValue<int> flag(#flag, def, false); \
|
||||
}
|
||||
|
||||
#define LUAU_DYNAMIC_FASTFLAG(flag) \
|
||||
|
@ -119,7 +110,7 @@ FValue<T>* FValue<T>::list = nullptr;
|
|||
#define LUAU_DYNAMIC_FASTFLAGVARIABLE(flag, def) \
|
||||
namespace DFFlag \
|
||||
{ \
|
||||
Luau::FValue<bool> flag(#flag, def, true, nullptr); \
|
||||
Luau::FValue<bool> flag(#flag, def, true); \
|
||||
}
|
||||
#define LUAU_DYNAMIC_FASTINT(flag) \
|
||||
namespace DFInt \
|
||||
|
@ -129,5 +120,5 @@ FValue<T>* FValue<T>::list = nullptr;
|
|||
#define LUAU_DYNAMIC_FASTINTVARIABLE(flag, def) \
|
||||
namespace DFInt \
|
||||
{ \
|
||||
Luau::FValue<int> flag(#flag, def, true, nullptr); \
|
||||
Luau::FValue<int> flag(#flag, def, true); \
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
// or critical bugs that are found after the code has been submitted.
|
||||
static const char* kList[] =
|
||||
{
|
||||
static const char* kList[] = {
|
||||
"LuauLowerBoundsCalculation",
|
||||
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)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Luau
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#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
|
||||
#define LUACODE_API extern
|
||||
#endif
|
||||
|
@ -35,5 +35,5 @@ struct lua_CompileOptions
|
|||
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);
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#include "Luau/Bytecode.h"
|
||||
#include "Luau/Compiler.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileRawlen, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace Compile
|
||||
|
@ -57,7 +55,7 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
|
|||
return LBF_RAWGET;
|
||||
if (builtin.isGlobal("rawequal"))
|
||||
return LBF_RAWEQUAL;
|
||||
if (FFlag::LuauCompileRawlen && builtin.isGlobal("rawlen"))
|
||||
if (builtin.isGlobal("rawlen"))
|
||||
return LBF_RAWLEN;
|
||||
|
||||
if (builtin.isGlobal("unpack"))
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <algorithm>
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileBytecodeV3, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -77,6 +79,10 @@ static int getOpLength(LuauOpcode op)
|
|||
case LOP_JUMPIFNOTEQK:
|
||||
case LOP_FASTCALL2:
|
||||
case LOP_FASTCALL2K:
|
||||
case LOP_JUMPXEQKNIL:
|
||||
case LOP_JUMPXEQKB:
|
||||
case LOP_JUMPXEQKN:
|
||||
case LOP_JUMPXEQKS:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
|
@ -108,6 +114,10 @@ inline bool isJumpD(LuauOpcode op)
|
|||
case LOP_JUMPBACK:
|
||||
case LOP_JUMPIFEQK:
|
||||
case LOP_JUMPIFNOTEQK:
|
||||
case LOP_JUMPXEQKNIL:
|
||||
case LOP_JUMPXEQKB:
|
||||
case LOP_JUMPXEQKN:
|
||||
case LOP_JUMPXEQKS:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -1069,6 +1079,9 @@ std::string BytecodeBuilder::getError(const std::string& message)
|
|||
|
||||
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
|
||||
return LBC_VERSION_TARGET;
|
||||
}
|
||||
|
@ -1246,6 +1259,24 @@ void BytecodeBuilder::validate() const
|
|||
VJUMP(LUAU_INSN_D(insn));
|
||||
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_SUB:
|
||||
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);
|
||||
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:
|
||||
LUAU_ASSERT(!"Unsupported opcode");
|
||||
}
|
||||
|
|
|
@ -26,9 +26,8 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
|||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileFoldBuiltins, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileBetterMultret, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileFreeReassign, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileXEQ, false)
|
||||
|
||||
LUAU_FASTFLAG(LuauInterpolatedStringBaseSupport)
|
||||
|
||||
|
@ -279,9 +278,6 @@ struct Compiler
|
|||
// returns true if node can return multiple values; may conservatively return true even if expr is known to return just a single value
|
||||
bool isExprMultRet(AstExpr* node)
|
||||
{
|
||||
if (!FFlag::LuauCompileBetterMultret)
|
||||
return node->is<AstExprCall>() || node->is<AstExprVarargs>();
|
||||
|
||||
AstExprCall* expr = node->as<AstExprCall>();
|
||||
if (!expr)
|
||||
return node->is<AstExprVarargs>();
|
||||
|
@ -313,27 +309,10 @@ struct Compiler
|
|||
if (AstExprCall* expr = node->as<AstExprCall>())
|
||||
{
|
||||
// Optimization: convert multret calls that always return one value to fixedret calls; this facilitates inlining/constant folding
|
||||
if (options.optimizationLevel >= 2)
|
||||
if (options.optimizationLevel >= 2 && !isExprMultRet(node))
|
||||
{
|
||||
if (FFlag::LuauCompileBetterMultret)
|
||||
{
|
||||
if (!isExprMultRet(node))
|
||||
{
|
||||
compileExprTemp(node, target);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AstExprFunction* func = getFunctionExpr(expr->func);
|
||||
Function* fi = func ? functions.find(func) : nullptr;
|
||||
|
||||
if (fi && fi->returnsOne)
|
||||
{
|
||||
compileExprTemp(node, target);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
compileExprTemp(node, target);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We temporarily swap out regTop to have targetTop work correctly...
|
||||
|
@ -1033,9 +1012,8 @@ struct Compiler
|
|||
size_t compileCompareJump(AstExprBinary* expr, bool not_ = false)
|
||||
{
|
||||
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* right = expr->right;
|
||||
|
||||
|
@ -1047,36 +1025,112 @@ struct Compiler
|
|||
std::swap(left, right);
|
||||
}
|
||||
|
||||
uint8_t rl = compileExprAuto(left, rs);
|
||||
int32_t rr = -1;
|
||||
|
||||
if (isEq && operandIsConstant)
|
||||
if (FFlag::LuauCompileXEQ)
|
||||
{
|
||||
if (opc == LOP_JUMPIFEQ)
|
||||
opc = LOP_JUMPIFEQK;
|
||||
else if (opc == LOP_JUMPIFNOTEQ)
|
||||
opc = LOP_JUMPIFNOTEQK;
|
||||
uint8_t rl = compileExprAuto(left, rs);
|
||||
|
||||
rr = getConstantIndex(right);
|
||||
LUAU_ASSERT(rr >= 0);
|
||||
}
|
||||
else
|
||||
rr = compileExprAuto(right, rs);
|
||||
if (isEq && operandIsConstant)
|
||||
{
|
||||
const Constant* cv = constants.find(right);
|
||||
LUAU_ASSERT(cv && cv->type != Constant::Type_Unknown);
|
||||
|
||||
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)
|
||||
{
|
||||
bytecode.emitAD(opc, uint8_t(rr), 0);
|
||||
bytecode.emitAux(rl);
|
||||
switch (cv->type)
|
||||
{
|
||||
case Constant::Type_Nil:
|
||||
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
|
||||
{
|
||||
bytecode.emitAD(opc, rl, 0);
|
||||
bytecode.emitAux(rr);
|
||||
}
|
||||
LuauOpcode opc = getJumpOpCompare(expr->op, not_);
|
||||
|
||||
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)
|
||||
|
@ -3514,30 +3568,7 @@ struct Compiler
|
|||
|
||||
bool visit(AstStatReturn* stat) override
|
||||
{
|
||||
if (FFlag::LuauCompileBetterMultret)
|
||||
{
|
||||
returnsOne &= stat->list.size == 1 && !self->isExprMultRet(stat->list.data[0]);
|
||||
}
|
||||
else if (stat->list.size == 1)
|
||||
{
|
||||
AstExpr* value = stat->list.data[0];
|
||||
|
||||
if (AstExprCall* expr = value->as<AstExprCall>())
|
||||
{
|
||||
AstExprFunction* func = self->getFunctionExpr(expr->func);
|
||||
Function* fi = func ? self->functions.find(func) : nullptr;
|
||||
|
||||
returnsOne &= fi && fi->returnsOne;
|
||||
}
|
||||
else if (value->is<AstExprVarargs>())
|
||||
{
|
||||
returnsOne = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnsOne = false;
|
||||
}
|
||||
returnsOne &= stat->list.size == 1 && !self->isExprMultRet(stat->list.data[0]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -3679,7 +3710,7 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
|||
trackValues(compiler.globals, compiler.variables, root);
|
||||
|
||||
// builtin folding is enabled on optimization level 2 since we can't deoptimize folding at runtime
|
||||
if (options.optimizationLevel >= 2 && FFlag::LuauCompileFoldBuiltins)
|
||||
if (options.optimizationLevel >= 2)
|
||||
compiler.builtinsFold = &compiler.builtins;
|
||||
|
||||
if (options.optimizationLevel >= 1)
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include <limits.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileModelBuiltins, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace Compile
|
||||
|
@ -155,7 +153,7 @@ struct CostVisitor : AstVisitor
|
|||
{
|
||||
// builtin cost modeling is different from regular calls because we use FASTCALL to compile these
|
||||
// thus we use a cheaper baseline, don't account for function, and assume constant/local copy is free
|
||||
bool builtin = FFlag::LuauCompileModelBuiltins && builtins.find(expr) != nullptr;
|
||||
bool builtin = builtins.find(expr) != nullptr;
|
||||
bool builtinShort = builtin && expr->args.size <= 2; // FASTCALL1/2
|
||||
|
||||
Cost cost = builtin ? 2 : 3;
|
||||
|
|
8
Makefile
8
Makefile
|
@ -55,6 +55,7 @@ ifneq ($(opt),)
|
|||
endif
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
aliases: luau luau-analyze
|
||||
aliases: $(EXECUTABLE_ALIASES)
|
||||
|
||||
test: $(TESTS_TARGET)
|
||||
$(TESTS_TARGET) $(TESTS_ARGS)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD)
|
||||
rm -rf luau luau-analyze
|
||||
rm -rf $(EXECUTABLE_ALIASES)
|
||||
|
||||
coverage: $(TESTS_TARGET)
|
||||
$(TESTS_TARGET) --fflags=true
|
||||
|
@ -154,6 +155,9 @@ luau: $(REPL_CLI_TARGET)
|
|||
luau-analyze: $(ANALYZE_CLI_TARGET)
|
||||
ln -fs $^ $@
|
||||
|
||||
luau-tests: $(TESTS_TARGET)
|
||||
ln -fs $^ $@
|
||||
|
||||
# executable targets
|
||||
$(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)
|
||||
|
|
|
@ -66,6 +66,8 @@ target_sources(Luau.CodeGen PRIVATE
|
|||
|
||||
# Luau.Analysis Sources
|
||||
target_sources(Luau.Analysis PRIVATE
|
||||
Analysis/include/Luau/ApplyTypeFunction.h
|
||||
Analysis/include/Luau/AstJsonEncoder.h
|
||||
Analysis/include/Luau/AstQuery.h
|
||||
Analysis/include/Luau/Autocomplete.h
|
||||
Analysis/include/Luau/BuiltinDefinitions.h
|
||||
|
@ -81,7 +83,7 @@ target_sources(Luau.Analysis PRIVATE
|
|||
Analysis/include/Luau/Frontend.h
|
||||
Analysis/include/Luau/Instantiation.h
|
||||
Analysis/include/Luau/IostreamHelpers.h
|
||||
Analysis/include/Luau/JsonEncoder.h
|
||||
Analysis/include/Luau/JsonEmitter.h
|
||||
Analysis/include/Luau/Linter.h
|
||||
Analysis/include/Luau/LValue.h
|
||||
Analysis/include/Luau/Module.h
|
||||
|
@ -113,6 +115,8 @@ target_sources(Luau.Analysis PRIVATE
|
|||
Analysis/include/Luau/Variant.h
|
||||
Analysis/include/Luau/VisitTypeVar.h
|
||||
|
||||
Analysis/src/ApplyTypeFunction.cpp
|
||||
Analysis/src/AstJsonEncoder.cpp
|
||||
Analysis/src/AstQuery.cpp
|
||||
Analysis/src/Autocomplete.cpp
|
||||
Analysis/src/BuiltinDefinitions.cpp
|
||||
|
@ -126,7 +130,7 @@ target_sources(Luau.Analysis PRIVATE
|
|||
Analysis/src/Frontend.cpp
|
||||
Analysis/src/Instantiation.cpp
|
||||
Analysis/src/IostreamHelpers.cpp
|
||||
Analysis/src/JsonEncoder.cpp
|
||||
Analysis/src/JsonEmitter.cpp
|
||||
Analysis/src/Linter.cpp
|
||||
Analysis/src/LValue.cpp
|
||||
Analysis/src/Module.cpp
|
||||
|
@ -255,6 +259,7 @@ if(TARGET Luau.UnitTest)
|
|||
tests/ScopedFlags.h
|
||||
tests/Fixture.cpp
|
||||
tests/AssemblyBuilderX64.test.cpp
|
||||
tests/AstJsonEncoder.test.cpp
|
||||
tests/AstQuery.test.cpp
|
||||
tests/AstVisitor.test.cpp
|
||||
tests/Autocomplete.test.cpp
|
||||
|
@ -266,7 +271,8 @@ if(TARGET Luau.UnitTest)
|
|||
tests/CostModel.test.cpp
|
||||
tests/Error.test.cpp
|
||||
tests/Frontend.test.cpp
|
||||
tests/JsonEncoder.test.cpp
|
||||
tests/JsonEmitter.test.cpp
|
||||
tests/Lexer.test.cpp
|
||||
tests/Linter.test.cpp
|
||||
tests/LValue.test.cpp
|
||||
tests/Module.test.cpp
|
||||
|
|
|
@ -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)
|
||||
|
||||
/*
|
||||
|
@ -23,7 +23,7 @@
|
|||
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX - (i))
|
||||
#define lua_ispseudo(i) ((i) <= LUA_REGISTRYINDEX)
|
||||
|
||||
/* thread status; 0 is OK */
|
||||
// thread status; 0 is OK
|
||||
enum lua_Status
|
||||
{
|
||||
LUA_OK = 0,
|
||||
|
@ -32,7 +32,7 @@ enum lua_Status
|
|||
LUA_ERRSYNTAX,
|
||||
LUA_ERRMEM,
|
||||
LUA_ERRERR,
|
||||
LUA_BREAK, /* yielded for a debug breakpoint */
|
||||
LUA_BREAK, // yielded for a debug breakpoint
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
/* non-return type */
|
||||
// non-return type
|
||||
#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
|
||||
enum lua_Type
|
||||
{
|
||||
LUA_TNIL = 0, /* must be 0 due to lua_isnoneornil */
|
||||
LUA_TBOOLEAN = 1, /* must be 1 due to l_isfalse */
|
||||
LUA_TNIL = 0, // must be 0 due to lua_isnoneornil
|
||||
LUA_TBOOLEAN = 1, // must be 1 due to l_isfalse
|
||||
|
||||
|
||||
LUA_TLIGHTUSERDATA,
|
||||
LUA_TNUMBER,
|
||||
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,
|
||||
|
@ -77,23 +77,23 @@ enum lua_Type
|
|||
LUA_TUSERDATA,
|
||||
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_TUPVAL,
|
||||
LUA_TDEADKEY,
|
||||
|
||||
/* the count of TValue type tags */
|
||||
// the count of TValue type tags
|
||||
LUA_T_COUNT = LUA_TPROTO
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/* type of numbers in Luau */
|
||||
// type of numbers in Luau
|
||||
typedef double lua_Number;
|
||||
|
||||
/* type for integer functions */
|
||||
// type for integer functions
|
||||
typedef int lua_Integer;
|
||||
|
||||
/* unsigned integer type */
|
||||
// unsigned integer type
|
||||
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_replace(lua_State* L, int idx);
|
||||
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_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
|
||||
{
|
||||
/* stop and resume incremental garbage collection */
|
||||
// stop and resume incremental garbage collection
|
||||
LUA_GCSTOP,
|
||||
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,
|
||||
|
||||
/* 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_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,
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
struct lua_Debug
|
||||
{
|
||||
const char* name; /* (n) */
|
||||
const char* what; /* (s) `Lua', `C', `main', `tail' */
|
||||
const char* source; /* (s) */
|
||||
int linedefined; /* (s) */
|
||||
int currentline; /* (l) */
|
||||
unsigned char nupvals; /* (u) number of upvalues */
|
||||
unsigned char nparams; /* (a) number of parameters */
|
||||
char isvararg; /* (a) */
|
||||
char short_src[LUA_IDSIZE]; /* (s) */
|
||||
void* userdata; /* only valid in luau_callhook */
|
||||
const char* name; // (n)
|
||||
const char* what; // (s) `Lua', `C', `main', `tail'
|
||||
const char* source; // (s)
|
||||
int linedefined; // (s)
|
||||
int currentline; // (l)
|
||||
unsigned char nupvals; // (u) number of upvalues
|
||||
unsigned char nparams; // (a) number of parameters
|
||||
char isvararg; // (a)
|
||||
char short_src[LUA_IDSIZE]; // (s)
|
||||
void* userdata; // only valid in luau_callhook
|
||||
};
|
||||
|
||||
/* }====================================================================== */
|
||||
// }======================================================================
|
||||
|
||||
/* Callbacks that can be used to reconfigure behavior of the VM dynamically.
|
||||
* 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 */
|
||||
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 (*panic)(lua_State* L, int errcode); /* gets called when an unprotected error is raised (if longjmp is used) */
|
||||
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 (*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 */
|
||||
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
|
||||
|
||||
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 (*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 (*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 (*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
|
||||
};
|
||||
typedef struct lua_Callbacks lua_Callbacks;
|
||||
|
||||
|
|
|
@ -33,14 +33,14 @@
|
|||
#define LUA_NORETURN __attribute__((__noreturn__))
|
||||
#endif
|
||||
|
||||
/* Can be used to reconfigure visibility/exports for public APIs */
|
||||
// Can be used to reconfigure visibility/exports for public APIs
|
||||
#ifndef LUA_API
|
||||
#define LUA_API extern
|
||||
#endif
|
||||
|
||||
#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__)
|
||||
#define LUAI_FUNC __attribute__((visibility("hidden"))) extern
|
||||
#define LUAI_DATA LUAI_FUNC
|
||||
|
@ -49,67 +49,67 @@
|
|||
#define LUAI_DATA extern
|
||||
#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
|
||||
#define LUA_USE_LONGJMP 0
|
||||
#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
|
||||
#define LUA_IDSIZE 256
|
||||
#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
|
||||
#define LUA_MINSTACK 20
|
||||
#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
|
||||
#define LUAI_MAXCSTACK 8000
|
||||
#endif
|
||||
|
||||
/* LUAI_MAXCALLS limits the number of nested calls */
|
||||
// LUAI_MAXCALLS limits the number of nested calls
|
||||
#ifndef LUAI_MAXCALLS
|
||||
#define LUAI_MAXCALLS 20000
|
||||
#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
|
||||
#define LUAI_MAXCCALLS 200
|
||||
#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
|
||||
#define LUA_BUFFERSIZE 512
|
||||
#endif
|
||||
|
||||
/* number of valid Lua userdata tags */
|
||||
// number of valid Lua userdata tags
|
||||
#ifndef LUA_UTAG_LIMIT
|
||||
#define LUA_UTAG_LIMIT 128
|
||||
#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
|
||||
#define LUA_SIZECLASSES 32
|
||||
#endif
|
||||
|
||||
/* available number of separate memory categories */
|
||||
// available number of separate memory categories
|
||||
#ifndef LUA_MEMORY_CATEGORIES
|
||||
#define LUA_MEMORY_CATEGORIES 256
|
||||
#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
|
||||
#define LUA_MINSTRTABSIZE 32
|
||||
#endif
|
||||
|
||||
/* maximum number of captures supported by pattern matching */
|
||||
// maximum number of captures supported by pattern matching
|
||||
#ifndef LUA_MAXCAPTURES
|
||||
#define LUA_MAXCAPTURES 32
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
// }==================================================================
|
||||
|
||||
/*
|
||||
@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment.
|
||||
|
@ -126,6 +126,6 @@
|
|||
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
|
||||
|
|
|
@ -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)))
|
||||
|
||||
/* generic buffer manipulation */
|
||||
// generic buffer manipulation
|
||||
|
||||
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_pushresultsize(luaL_Buffer* B, size_t size);
|
||||
|
||||
/* builtin libraries */
|
||||
// builtin libraries
|
||||
LUALIB_API int luaopen_base(lua_State* L);
|
||||
|
||||
#define LUA_COLIBNAME "coroutine"
|
||||
|
@ -129,9 +129,9 @@ LUALIB_API int luaopen_math(lua_State* L);
|
|||
#define LUA_DBLIBNAME "debug"
|
||||
LUALIB_API int luaopen_debug(lua_State* L);
|
||||
|
||||
/* open all builtin libraries */
|
||||
// open all builtin libraries
|
||||
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_sandboxthread(lua_State* L);
|
||||
|
|
|
@ -34,8 +34,6 @@
|
|||
* therefore call luaC_checkGC before luaC_checkthreadsleep to guarantee the object is pushed to an awake thread.
|
||||
*/
|
||||
|
||||
LUAU_FASTFLAG(LuauLazyAtoms)
|
||||
|
||||
const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio $\n"
|
||||
"$Authors: R. Ierusalimschy, L. H. de Figueiredo & W. Celes $\n"
|
||||
"$URL: www.lua.org $\n";
|
||||
|
@ -54,7 +52,6 @@ const char* luau_ident = "$Luau: Copyright (C) 2019-2022 Roblox Corporation $\n"
|
|||
}
|
||||
|
||||
#define updateatom(L, ts) \
|
||||
if (FFlag::LuauLazyAtoms) \
|
||||
{ \
|
||||
if (ts->atom == ATOM_UNDEF) \
|
||||
ts->atom = L->global->cb.useratom ? L->global->cb.useratom(ts->data, ts->len) : -1; \
|
||||
|
@ -62,8 +59,8 @@ const char* luau_ident = "$Luau: Copyright (C) 2019-2022 Roblox Corporation $\n"
|
|||
|
||||
static Table* getcurrenv(lua_State* L)
|
||||
{
|
||||
if (L->ci == L->base_ci) /* no enclosing function? */
|
||||
return L->gt; /* use global table as environment */
|
||||
if (L->ci == L->base_ci) // no enclosing function?
|
||||
return L->gt; // use global table as environment
|
||||
else
|
||||
return curr_func(L)->env;
|
||||
}
|
||||
|
@ -72,7 +69,7 @@ static LUAU_NOINLINE TValue* pseudo2addr(lua_State* L, int idx)
|
|||
{
|
||||
api_check(L, lua_ispseudo(idx));
|
||||
switch (idx)
|
||||
{ /* pseudo-indices */
|
||||
{ // pseudo-indices
|
||||
case LUA_REGISTRYINDEX:
|
||||
return registry(L);
|
||||
case LUA_ENVIRONINDEX:
|
||||
|
@ -132,7 +129,7 @@ int lua_checkstack(lua_State* L, int size)
|
|||
{
|
||||
int res = 1;
|
||||
if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK)
|
||||
res = 0; /* stack overflow */
|
||||
res = 0; // stack overflow
|
||||
else if (size > 0)
|
||||
{
|
||||
luaD_checkstack(L, size);
|
||||
|
@ -222,7 +219,7 @@ void lua_settop(lua_State* L, int idx)
|
|||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -270,7 +267,7 @@ void lua_replace(lua_State* L, int idx)
|
|||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
L->top--;
|
||||
|
@ -432,13 +429,13 @@ const char* lua_tolstring(lua_State* L, int idx, size_t* len)
|
|||
{
|
||||
luaC_checkthreadsleep(L);
|
||||
if (!luaV_tostring(L, o))
|
||||
{ /* conversion failed? */
|
||||
{ // conversion failed?
|
||||
if (len != NULL)
|
||||
*len = 0;
|
||||
return NULL;
|
||||
}
|
||||
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)
|
||||
*len = tsvalue(o)->len;
|
||||
|
@ -663,7 +660,7 @@ void lua_pushcclosurek(lua_State* L, lua_CFunction fn, const char* debugname, in
|
|||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
@ -832,7 +829,7 @@ void lua_settable(lua_State* L, int idx)
|
|||
StkId t = index2addr(L, idx);
|
||||
api_checkvalidindex(L, t);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -970,7 +967,7 @@ void lua_call(lua_State* L, int nargs, int nresults)
|
|||
** Execute a protected call.
|
||||
*/
|
||||
struct CallS
|
||||
{ /* data to `f_call' */
|
||||
{ // data to `f_call'
|
||||
StkId func;
|
||||
int nresults;
|
||||
};
|
||||
|
@ -995,7 +992,7 @@ int lua_pcall(lua_State* L, int nargs, int nresults, int errfunc)
|
|||
func = savestack(L, o);
|
||||
}
|
||||
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;
|
||||
|
||||
int status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
|
||||
|
@ -1047,7 +1044,7 @@ int lua_gc(lua_State* L, int what, int data)
|
|||
}
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
@ -1087,8 +1084,8 @@ int lua_gc(lua_State* L, int what, int data)
|
|||
actualwork += stepsize;
|
||||
|
||||
if (g->gcstate == GCSpause)
|
||||
{ /* end of cycle? */
|
||||
res = 1; /* signal it */
|
||||
{ // end of cycle?
|
||||
res = 1; // signal it
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1140,13 +1137,13 @@ int lua_gc(lua_State* L, int what, int data)
|
|||
}
|
||||
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;
|
||||
g->gcstepsize = data << 10;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
res = -1; /* invalid option */
|
||||
res = -1; // invalid option
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -1172,8 +1169,8 @@ int lua_next(lua_State* L, int idx)
|
|||
{
|
||||
api_incr_top(L);
|
||||
}
|
||||
else /* no more elements */
|
||||
L->top -= 1; /* remove key */
|
||||
else // no more elements
|
||||
L->top -= 1; // remove key
|
||||
return more;
|
||||
}
|
||||
|
||||
|
@ -1188,12 +1185,12 @@ void lua_concat(lua_State* L, int n)
|
|||
L->top -= (n - 1);
|
||||
}
|
||||
else if (n == 0)
|
||||
{ /* push empty string */
|
||||
{ // push empty string
|
||||
luaC_checkthreadsleep(L);
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
|
||||
api_incr_top(L);
|
||||
}
|
||||
/* else n == 1; nothing to do */
|
||||
// else n == 1; nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1280,7 +1277,7 @@ uintptr_t lua_encodepointer(lua_State* L, uintptr_t p)
|
|||
|
||||
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;
|
||||
global_State* g = L->global;
|
||||
StkId p = index2addr(L, idx);
|
||||
|
@ -1289,13 +1286,13 @@ int lua_ref(lua_State* L, int idx)
|
|||
Table* reg = hvalue(registry(L));
|
||||
|
||||
if (g->registryfree != 0)
|
||||
{ /* reuse existing slot */
|
||||
{ // reuse existing slot
|
||||
ref = g->registryfree;
|
||||
}
|
||||
else
|
||||
{ /* no free elements */
|
||||
{ // no free elements
|
||||
ref = luaH_getn(reg);
|
||||
ref++; /* create new reference */
|
||||
ref++; // create new reference
|
||||
}
|
||||
|
||||
TValue* slot = luaH_setnum(L, reg, ref);
|
||||
|
@ -1315,7 +1312,7 @@ void lua_unref(lua_State* L, int ref)
|
|||
global_State* g = L->global;
|
||||
Table* reg = hvalue(registry(L));
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#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)
|
||||
|
||||
/*
|
||||
|
@ -75,7 +75,7 @@ void luaL_where(lua_State* L, int level)
|
|||
lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
|
||||
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, ...)
|
||||
|
@ -89,7 +89,7 @@ l_noret luaL_errorL(lua_State* L, const char* fmt, ...)
|
|||
lua_error(L);
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
// }======================================================
|
||||
|
||||
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)
|
||||
{
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */
|
||||
if (!lua_isnil(L, -1)) /* name already in use? */
|
||||
return 0; /* leave previous value on top, but return 0 */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, tname); // get registry.name
|
||||
if (!lua_isnil(L, -1)) // name already in use?
|
||||
return 0; // leave previous value on top, but return 0
|
||||
lua_pop(L, 1);
|
||||
lua_newtable(L); /* create metatable */
|
||||
lua_newtable(L); // create metatable
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, tname); // registry.name = metatable
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -118,18 +118,18 @@ void* luaL_checkudata(lua_State* L, int ud, const char* tname)
|
|||
{
|
||||
void* p = lua_touserdata(L, ud);
|
||||
if (p != NULL)
|
||||
{ /* value is a userdata? */
|
||||
{ // value is a userdata?
|
||||
if (lua_getmetatable(L, ud))
|
||||
{ /* does it have a metatable? */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
|
||||
{ // does it have a metatable?
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, tname); // get correct metatable
|
||||
if (lua_rawequal(L, -1, -2))
|
||||
{ /* does it have the correct mt? */
|
||||
lua_pop(L, 2); /* remove both metatables */
|
||||
{ // does it have the correct mt?
|
||||
lua_pop(L, 2); // remove both metatables
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
if (!lua_getmetatable(L, obj)) /* no metatable? */
|
||||
if (!lua_getmetatable(L, obj)) // no metatable?
|
||||
return 0;
|
||||
lua_pushstring(L, event);
|
||||
lua_rawget(L, -2);
|
||||
if (lua_isnil(L, -1))
|
||||
{
|
||||
lua_pop(L, 2); /* remove metatable and metafield */
|
||||
lua_pop(L, 2); // remove metatable and metafield
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_remove(L, -2); /* remove only metatable */
|
||||
lua_remove(L, -2); // remove only metatable
|
||||
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)
|
||||
{
|
||||
obj = abs_index(L, obj);
|
||||
if (!luaL_getmetafield(L, obj, event)) /* no metafield? */
|
||||
if (!luaL_getmetafield(L, obj, event)) // no metafield?
|
||||
return 0;
|
||||
lua_pushvalue(L, obj);
|
||||
lua_call(L, 1, 1);
|
||||
|
@ -282,19 +282,19 @@ void luaL_register(lua_State* L, const char* libname, const luaL_Reg* l)
|
|||
if (libname)
|
||||
{
|
||||
int size = libsize(l);
|
||||
/* check whether lib already exists */
|
||||
// check whether lib already exists
|
||||
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))
|
||||
{ /* not found? */
|
||||
lua_pop(L, 1); /* remove previous result */
|
||||
/* try global variable (and create one if it does not exist) */
|
||||
{ // not found?
|
||||
lua_pop(L, 1); // remove previous result
|
||||
// try global variable (and create one if it does not exist)
|
||||
if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)
|
||||
luaL_error(L, "name conflict for module '%s'", libname);
|
||||
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++)
|
||||
{
|
||||
|
@ -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_rawget(L, -2);
|
||||
if (lua_isnil(L, -1))
|
||||
{ /* no such field? */
|
||||
lua_pop(L, 1); /* remove this nil */
|
||||
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
|
||||
{ // no such field?
|
||||
lua_pop(L, 1); // remove this nil
|
||||
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); // new table for field
|
||||
lua_pushlstring(L, fname, e - fname);
|
||||
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))
|
||||
{ /* field has a non-table value? */
|
||||
lua_pop(L, 2); /* remove table and value */
|
||||
return fname; /* return problematic part of the name */
|
||||
{ // field has a non-table value?
|
||||
lua_pop(L, 2); // remove table and value
|
||||
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;
|
||||
} while (*e == '.');
|
||||
return NULL;
|
||||
|
@ -470,11 +470,11 @@ void luaL_pushresultsize(luaL_Buffer* B, size_t size)
|
|||
luaL_pushresult(B);
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
// }======================================================
|
||||
|
||||
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))
|
||||
luaL_error(L, "'__tostring' must return a string");
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauLenTM)
|
||||
|
||||
static void writestring(const char* s, size_t l)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int n = lua_gettop(L); // number of arguments
|
||||
for (int i = 1; i <= n; i++)
|
||||
{
|
||||
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)
|
||||
writestring("\t", 1);
|
||||
writestring(s, l);
|
||||
lua_pop(L, 1); /* pop result */
|
||||
lua_pop(L, 1); // pop result
|
||||
}
|
||||
writestring("\n", 1);
|
||||
return 0;
|
||||
|
@ -38,7 +36,7 @@ static int luaB_tonumber(lua_State* L)
|
|||
{
|
||||
int base = luaL_optinteger(L, 2, 10);
|
||||
if (base == 10)
|
||||
{ /* standard conversion */
|
||||
{ // standard conversion
|
||||
int isnum = 0;
|
||||
double n = lua_tonumberx(L, 1, &isnum);
|
||||
if (isnum)
|
||||
|
@ -46,7 +44,7 @@ static int luaB_tonumber(lua_State* L)
|
|||
lua_pushnumber(L, n);
|
||||
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
|
||||
{
|
||||
|
@ -56,17 +54,17 @@ static int luaB_tonumber(lua_State* L)
|
|||
unsigned long long n;
|
||||
n = strtoull(s1, &s2, base);
|
||||
if (s1 != s2)
|
||||
{ /* at least one valid digit? */
|
||||
{ // at least one valid digit?
|
||||
while (isspace((unsigned char)(*s2)))
|
||||
s2++; /* skip trailing spaces */
|
||||
s2++; // skip trailing spaces
|
||||
if (*s2 == '\0')
|
||||
{ /* no invalid trailing characters? */
|
||||
{ // no invalid trailing characters?
|
||||
lua_pushnumber(L, (double)n);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_pushnil(L); /* else not a number */
|
||||
lua_pushnil(L); // else not a number
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -75,7 +73,7 @@ static int luaB_error(lua_State* L)
|
|||
int level = luaL_optinteger(L, 2, 1);
|
||||
lua_settop(L, 1);
|
||||
if (lua_isstring(L, 1) && level > 0)
|
||||
{ /* add extra information? */
|
||||
{ // add extra information?
|
||||
luaL_where(L, level);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_concat(L, 2);
|
||||
|
@ -89,10 +87,10 @@ static int luaB_getmetatable(lua_State* L)
|
|||
if (!lua_getmetatable(L, 1))
|
||||
{
|
||||
lua_pushnil(L);
|
||||
return 1; /* no metatable */
|
||||
return 1; // no 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)
|
||||
|
@ -126,8 +124,8 @@ static void getfunc(lua_State* L, int opt)
|
|||
static int luaB_getfenv(lua_State* L)
|
||||
{
|
||||
getfunc(L, 1);
|
||||
if (lua_iscfunction(L, -1)) /* is a C function? */
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */
|
||||
if (lua_iscfunction(L, -1)) // is a C function?
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX); // return the thread's global env.
|
||||
else
|
||||
lua_getfenv(L, -1);
|
||||
lua_setsafeenv(L, -1, false);
|
||||
|
@ -142,7 +140,7 @@ static int luaB_setfenv(lua_State* L)
|
|||
lua_setsafeenv(L, -1, false);
|
||||
if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0)
|
||||
{
|
||||
/* change environment of current thread */
|
||||
// change environment of current thread
|
||||
lua_pushthread(L);
|
||||
lua_insert(L, -2);
|
||||
lua_setfenv(L, -2);
|
||||
|
@ -182,9 +180,6 @@ static int luaB_rawset(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);
|
||||
luaL_argcheck(L, tt == LUA_TTABLE || tt == LUA_TSTRING, 1, "table or string expected");
|
||||
int len = lua_objlen(L, 1);
|
||||
|
@ -201,7 +196,7 @@ static int luaB_gcinfo(lua_State* L)
|
|||
static int luaB_type(lua_State* L)
|
||||
{
|
||||
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)));
|
||||
return 1;
|
||||
}
|
||||
|
@ -209,7 +204,7 @@ static int luaB_type(lua_State* L)
|
|||
static int luaB_typeof(lua_State* L)
|
||||
{
|
||||
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));
|
||||
return 1;
|
||||
}
|
||||
|
@ -217,7 +212,7 @@ static int luaB_typeof(lua_State* L)
|
|||
int luaB_next(lua_State* L)
|
||||
{
|
||||
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))
|
||||
return 2;
|
||||
else
|
||||
|
@ -230,9 +225,9 @@ int luaB_next(lua_State* L)
|
|||
static int luaB_pairs(lua_State* L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
|
||||
lua_pushvalue(L, 1); /* state, */
|
||||
lua_pushnil(L); /* and initial value */
|
||||
lua_pushvalue(L, lua_upvalueindex(1)); // return generator,
|
||||
lua_pushvalue(L, 1); // state,
|
||||
lua_pushnil(L); // and initial value
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
@ -240,7 +235,7 @@ int luaB_inext(lua_State* L)
|
|||
{
|
||||
int i = luaL_checkinteger(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
i++; /* next value */
|
||||
i++; // next value
|
||||
lua_pushinteger(L, i);
|
||||
lua_rawgeti(L, 1, i);
|
||||
return (lua_isnil(L, -1)) ? 0 : 2;
|
||||
|
@ -249,9 +244,9 @@ int luaB_inext(lua_State* L)
|
|||
static int luaB_ipairs(lua_State* L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
|
||||
lua_pushvalue(L, 1); /* state, */
|
||||
lua_pushinteger(L, 0); /* and initial value */
|
||||
lua_pushvalue(L, lua_upvalueindex(1)); // return generator,
|
||||
lua_pushvalue(L, 1); // state,
|
||||
lua_pushinteger(L, 0); // and initial value
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
@ -340,12 +335,12 @@ static int luaB_xpcally(lua_State* L)
|
|||
{
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
|
||||
/* swap function & error function */
|
||||
// swap function & error function
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushvalue(L, 2);
|
||||
lua_replace(L, 1);
|
||||
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
|
||||
L->ci->flags |= LUA_CALLINFO_HANDLE;
|
||||
|
@ -386,7 +381,7 @@ static int luaB_xpcallcont(lua_State* L, int status)
|
|||
lua_rawcheckstack(L, 1);
|
||||
lua_pushboolean(L, true);
|
||||
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
|
||||
{
|
||||
|
@ -462,16 +457,16 @@ static void auxopen(lua_State* L, const char* name, lua_CFunction f, lua_CFuncti
|
|||
|
||||
int luaopen_base(lua_State* L)
|
||||
{
|
||||
/* set global _G */
|
||||
// set global _G
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
lua_setglobal(L, "_G");
|
||||
|
||||
/* open lib into global table */
|
||||
// open lib into global table
|
||||
luaL_register(L, "_G", base_funcs);
|
||||
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, "pairs", luaB_pairs, luaB_next);
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
#define ALLONES ~0u
|
||||
#define NBITS int(8 * sizeof(unsigned))
|
||||
|
||||
/* macro to trim extra bits */
|
||||
// macro to trim extra bits
|
||||
#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)))
|
||||
|
||||
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)
|
||||
{
|
||||
if (i < 0)
|
||||
{ /* shift right? */
|
||||
{ // shift right?
|
||||
i = -i;
|
||||
r = trim(r);
|
||||
if (i >= NBITS)
|
||||
|
@ -78,7 +78,7 @@ static int b_shift(lua_State* L, b_uint r, int i)
|
|||
r >>= i;
|
||||
}
|
||||
else
|
||||
{ /* shift left */
|
||||
{ // shift left
|
||||
if (i >= NBITS)
|
||||
r = 0;
|
||||
else
|
||||
|
@ -106,11 +106,11 @@ static int b_arshift(lua_State* L)
|
|||
if (i < 0 || !(r & ((b_uint)1 << (NBITS - 1))))
|
||||
return b_shift(L, r, -i);
|
||||
else
|
||||
{ /* arithmetic shift for 'negative' number */
|
||||
{ // arithmetic shift for 'negative' number
|
||||
if (i >= NBITS)
|
||||
r = ALLONES;
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
@ -119,9 +119,9 @@ static int b_arshift(lua_State* L)
|
|||
static int b_rot(lua_State* L, int i)
|
||||
{
|
||||
b_uint r = luaL_checkunsigned(L, 1);
|
||||
i &= (NBITS - 1); /* i = i % NBITS */
|
||||
i &= (NBITS - 1); // i = i % NBITS
|
||||
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));
|
||||
lua_pushunsigned(L, trim(r));
|
||||
return 1;
|
||||
|
@ -172,7 +172,7 @@ static int b_replace(lua_State* L)
|
|||
b_uint v = luaL_checkunsigned(L, 2);
|
||||
int f = fieldargs(L, 3, &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);
|
||||
lua_pushunsigned(L, r);
|
||||
return 1;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
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 api_check(l, e) LUAU_ASSERT(e)
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
#include "lstate.h"
|
||||
#include "lvm.h"
|
||||
|
||||
#define CO_RUN 0 /* running */
|
||||
#define CO_SUS 1 /* suspended */
|
||||
#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */
|
||||
#define CO_RUN 0 // running
|
||||
#define CO_SUS 1 // suspended
|
||||
#define CO_NOR 2 // 'normal' (it resumed another coroutine)
|
||||
#define CO_DEAD 3
|
||||
|
||||
#define CO_STATUS_ERROR -1
|
||||
|
@ -23,13 +23,13 @@ static int auxstatus(lua_State* L, lua_State* co)
|
|||
return CO_SUS;
|
||||
if (co->status == LUA_BREAK)
|
||||
return CO_NOR;
|
||||
if (co->status != 0) /* some error occurred */
|
||||
if (co->status != 0) // some error occurred
|
||||
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;
|
||||
if (co->top == co->base)
|
||||
return CO_DEAD;
|
||||
return CO_SUS; /* initial state */
|
||||
return CO_SUS; // initial state
|
||||
}
|
||||
|
||||
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);
|
||||
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))
|
||||
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;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ static int auxresume(lua_State* L, lua_State* co, int narg)
|
|||
}
|
||||
else
|
||||
{
|
||||
lua_xmove(co, L, 1); /* move error message */
|
||||
lua_xmove(co, L, 1); // move error message
|
||||
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);
|
||||
if (!lua_checkstack(L, nres + 1))
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_rawcheckstack(L, 2);
|
||||
lua_xmove(co, L, 1); /* move error message */
|
||||
lua_xmove(co, L, 1); // move error message
|
||||
return CO_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
|
@ -119,13 +119,13 @@ static int coresumefinish(lua_State* L, int r)
|
|||
{
|
||||
lua_pushboolean(L, 0);
|
||||
lua_insert(L, -2);
|
||||
return 2; /* return false + error message */
|
||||
return 2; // return false + error message
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_pushboolean(L, 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 (lua_isstring(L, -1))
|
||||
{ /* error object is a string? */
|
||||
luaL_where(L, 1); /* add extra info */
|
||||
{ // error object is a string?
|
||||
luaL_where(L, 1); // add extra info
|
||||
lua_insert(L, -2);
|
||||
lua_concat(L, 2);
|
||||
}
|
||||
lua_error(L); /* propagate error */
|
||||
lua_error(L); // propagate error
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ static int coyield(lua_State* L)
|
|||
static int corunning(lua_State* 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;
|
||||
}
|
||||
|
||||
|
@ -250,7 +250,7 @@ static int coclose(lua_State* L)
|
|||
{
|
||||
lua_pushboolean(L, false);
|
||||
if (lua_gettop(co))
|
||||
lua_xmove(co, L, 1); /* move error message */
|
||||
lua_xmove(co, L, 1); // move error message
|
||||
lua_resetthread(co);
|
||||
return 2;
|
||||
}
|
||||
|
|
|
@ -82,9 +82,9 @@ static int db_info(lua_State* L)
|
|||
|
||||
case 'f':
|
||||
if (L1 == L)
|
||||
lua_pushvalue(L, -1 - results); /* function is right before results */
|
||||
lua_pushvalue(L, -1 - results); // function is right before results
|
||||
else
|
||||
lua_xmove(L1, L, 1); /* function is at top of L1 */
|
||||
lua_xmove(L1, L, 1); // function is at top of L1
|
||||
results++;
|
||||
break;
|
||||
|
||||
|
@ -130,15 +130,14 @@ static int db_traceback(lua_State* L)
|
|||
|
||||
if (ar.currentline > 0)
|
||||
{
|
||||
char line[32];
|
||||
#ifdef _MSC_VER
|
||||
_itoa(ar.currentline, line, 10); // 5x faster than sprintf
|
||||
#else
|
||||
sprintf(line, "%d", ar.currentline);
|
||||
#endif
|
||||
char line[32]; // manual conversion for performance
|
||||
char* lineend = line + sizeof(line);
|
||||
char* lineptr = lineend;
|
||||
for (unsigned int r = ar.currentline; r > 0; r /= 10)
|
||||
*--lineptr = '0' + (r % 10);
|
||||
|
||||
luaL_addchar(&buf, ':');
|
||||
luaL_addstring(&buf, line);
|
||||
luaL_addlstring(&buf, lineptr, lineend - lineptr);
|
||||
}
|
||||
|
||||
if (ar.name)
|
||||
|
|
|
@ -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;
|
||||
if (var)
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
int line = currentline(L, ci);
|
||||
luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
|
||||
|
@ -529,7 +536,7 @@ const char* lua_debugtrace(lua_State* L)
|
|||
if (ar.currentline > 0)
|
||||
{
|
||||
char line[32];
|
||||
sprintf(line, ":%d", ar.currentline);
|
||||
snprintf(line, sizeof(line), ":%d", ar.currentline);
|
||||
|
||||
offset = append(buf, sizeof(buf), offset, line);
|
||||
}
|
||||
|
@ -545,7 +552,7 @@ const char* lua_debugtrace(lua_State* L)
|
|||
if (depth > limit1 + limit2 && level == limit1 - 1)
|
||||
{
|
||||
char skip[32];
|
||||
sprintf(skip, "... (+%d frames)\n", int(depth - limit1 - limit2));
|
||||
snprintf(skip, sizeof(skip), "... (+%d frames)\n", int(depth - limit1 - limit2));
|
||||
|
||||
offset = append(buf, sizeof(buf), offset, skip);
|
||||
|
||||
|
|
|
@ -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_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_methoderror(lua_State* L, const TValue* p1, const TString* p2);
|
||||
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, ...);
|
||||
|
|
|
@ -31,7 +31,7 @@ struct lua_jmpbuf
|
|||
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__)
|
||||
#define LUAU_SETJMP(buf) _setjmp(buf)
|
||||
#define LUAU_LONGJMP(buf, code) _longjmp(buf, code)
|
||||
|
@ -153,7 +153,7 @@ l_noret luaD_throw(lua_State* L, int errcode)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* }====================================================== */
|
||||
// }======================================================
|
||||
|
||||
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);
|
||||
TValue* newstack = L->stack;
|
||||
for (int i = L->stacksize; i < realsize; i++)
|
||||
setnilvalue(newstack + i); /* erase new segment */
|
||||
setnilvalue(newstack + i); // erase new segment
|
||||
L->stacksize = realsize;
|
||||
L->stack_last = newstack + newsize;
|
||||
correctstack(L, oldstack);
|
||||
|
@ -194,7 +194,7 @@ void luaD_reallocCI(lua_State* L, int newsize)
|
|||
|
||||
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);
|
||||
else
|
||||
luaD_reallocstack(L, L->stacksize + n);
|
||||
|
@ -202,11 +202,11 @@ void luaD_growstack(lua_State* L, int n)
|
|||
|
||||
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);
|
||||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
/* 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);
|
||||
|
||||
if (L->nCcalls == LUAI_MAXCCALLS)
|
||||
luaG_runerror(L, "C stack overflow");
|
||||
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);
|
||||
|
||||
if (luau_precall(L, func, nResults) == PCRLUA)
|
||||
{ /* is a Lua function? */
|
||||
L->ci->flags |= LUA_CALLINFO_RETURN; /* luau_execute will stop after returning from the stack frame */
|
||||
{ // is a Lua function?
|
||||
L->ci->flags |= LUA_CALLINFO_RETURN; // luau_execute will stop after returning from the stack frame
|
||||
|
||||
int oldactive = luaC_threadactive(L);
|
||||
l_setbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
luaC_checkthreadsleep(L);
|
||||
|
||||
luau_execute(L); /* call it */
|
||||
luau_execute(L); // call it
|
||||
|
||||
if (!oldactive)
|
||||
resetbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
|
@ -263,18 +263,18 @@ static void seterrorobj(lua_State* L, int errcode, StkId oldtop)
|
|||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
case LUA_ERRSYNTAX:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -430,8 +430,8 @@ static void resume_finish(lua_State* L, int status)
|
|||
resetbit(L->stackstate, THREAD_ACTIVEBIT);
|
||||
|
||||
if (status != 0)
|
||||
{ /* error? */
|
||||
L->status = cast_byte(status); /* mark thread as `dead' */
|
||||
{ // error?
|
||||
L->status = cast_byte(status); // mark thread as `dead'
|
||||
seterrorobj(L, status, L->top);
|
||||
L->ci->top = L->top;
|
||||
}
|
||||
|
@ -503,7 +503,7 @@ int lua_yield(lua_State* L, int nresults)
|
|||
{
|
||||
if (L->nCcalls > L->baseCcalls)
|
||||
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;
|
||||
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);
|
||||
if (L->size_ci > LUAI_MAXCALLS)
|
||||
{ /* there was an overflow? */
|
||||
{ // there was an overflow?
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
luaF_close(L, oldtop); /* close eventual pending closures */
|
||||
luaF_close(L, oldtop); // close eventual pending closures
|
||||
seterrorobj(L, status, oldtop);
|
||||
L->ci = restoreci(L, old_ci);
|
||||
L->base = L->ci->base;
|
||||
|
|
10
VM/src/ldo.h
10
VM/src/ldo.h
|
@ -34,12 +34,12 @@
|
|||
#define saveci(L, p) ((char*)(p) - (char*)L->base_ci)
|
||||
#define restoreci(L, n) ((CallInfo*)((char*)L->base_ci + (n)))
|
||||
|
||||
/* results from luaD_precall */
|
||||
#define PCRLUA 0 /* initiated a call to a Lua function */
|
||||
#define PCRC 1 /* did a call to a C function */
|
||||
#define PCRYIELD 2 /* C function yielded */
|
||||
// results from luaD_precall
|
||||
#define PCRLUA 0 // initiated a call to a Lua function
|
||||
#define PCRC 1 // did a call to a C function
|
||||
#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);
|
||||
|
||||
LUAI_FUNC CallInfo* luaD_growCI(lua_State* L);
|
||||
|
|
|
@ -73,20 +73,20 @@ UpVal* luaF_findupval(lua_State* L, StkId level)
|
|||
{
|
||||
LUAU_ASSERT(p->v != &p->u.value);
|
||||
if (p->v == level)
|
||||
{ /* found a corresponding upvalue? */
|
||||
if (isdead(g, obj2gco(p))) /* is it dead? */
|
||||
changewhite(obj2gco(p)); /* resurrect it */
|
||||
{ // found a corresponding upvalue?
|
||||
if (isdead(g, obj2gco(p))) // is it dead?
|
||||
changewhite(obj2gco(p)); // resurrect it
|
||||
return p;
|
||||
}
|
||||
|
||||
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->marked = luaC_white(g);
|
||||
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
|
||||
UpVal* next = *pp;
|
||||
|
@ -121,9 +121,9 @@ void luaF_unlinkupval(UpVal* uv)
|
|||
|
||||
void luaF_freeupval(lua_State* L, UpVal* uv, lua_Page* page)
|
||||
{
|
||||
if (uv->v != &uv->u.value) /* is it open? */
|
||||
luaF_unlinkupval(uv); /* remove from open list */
|
||||
luaM_freegco(L, uv, sizeof(UpVal), uv->memcat, page); /* free upvalue */
|
||||
if (uv->v != &uv->u.value) // is it open?
|
||||
luaF_unlinkupval(uv); // remove from open list
|
||||
luaM_freegco(L, uv, sizeof(UpVal), uv->memcat, page); // free upvalue
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
if (pc >= f->locvars[i].startpc && pc < f->locvars[i].endpc)
|
||||
{ /* is variable active? */
|
||||
{ // is variable active?
|
||||
local_number--;
|
||||
if (local_number == 0)
|
||||
return &f->locvars[i];
|
||||
}
|
||||
}
|
||||
return NULL; /* not found */
|
||||
return NULL; // not found
|
||||
}
|
||||
|
|
130
VM/src/lgc.cpp
130
VM/src/lgc.cpp
|
@ -125,7 +125,7 @@ static void removeentry(LuaNode* n)
|
|||
{
|
||||
LUAU_ASSERT(ttisnil(gval(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)
|
||||
|
@ -141,7 +141,7 @@ static void reallymarkobject(global_State* g, GCObject* o)
|
|||
case LUA_TUSERDATA:
|
||||
{
|
||||
Table* mt = gco2u(o)->metatable;
|
||||
gray2black(o); /* udata are never gray */
|
||||
gray2black(o); // udata are never gray
|
||||
if (mt)
|
||||
markobject(g, mt);
|
||||
return;
|
||||
|
@ -150,8 +150,8 @@ static void reallymarkobject(global_State* g, GCObject* o)
|
|||
{
|
||||
UpVal* uv = gco2uv(o);
|
||||
markvalue(g, uv->v);
|
||||
if (uv->v == &uv->u.value) /* closed? */
|
||||
gray2black(o); /* open upvalues are never black */
|
||||
if (uv->v == &uv->u.value) // closed?
|
||||
gray2black(o); // open upvalues are never black
|
||||
return;
|
||||
}
|
||||
case LUA_TFUNCTION:
|
||||
|
@ -201,15 +201,15 @@ static int traversetable(global_State* g, Table* h)
|
|||
if (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))
|
||||
{
|
||||
weakkey = (strchr(modev, 'k') != NULL);
|
||||
weakvalue = (strchr(modev, 'v') != NULL);
|
||||
if (weakkey || weakvalue)
|
||||
{ /* is really weak? */
|
||||
h->gclist = g->weak; /* must be cleared after GC, ... */
|
||||
g->weak = obj2gco(h); /* ... so put in the appropriate list */
|
||||
{ // is really weak?
|
||||
h->gclist = g->weak; // must be cleared after GC, ...
|
||||
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);
|
||||
LUAU_ASSERT(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
|
||||
if (ttisnil(gval(n)))
|
||||
removeentry(n); /* remove empty entries */
|
||||
removeentry(n); // remove empty entries
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(!ttisnil(gkey(n)));
|
||||
|
@ -251,20 +251,20 @@ static void traverseproto(global_State* g, Proto* f)
|
|||
stringmark(f->source);
|
||||
if (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]);
|
||||
for (i = 0; i < f->sizeupvalues; i++)
|
||||
{ /* mark upvalue names */
|
||||
{ // mark upvalue names
|
||||
if (f->upvalues[i])
|
||||
stringmark(f->upvalues[i]);
|
||||
}
|
||||
for (i = 0; i < f->sizep; i++)
|
||||
{ /* mark nested protos */
|
||||
{ // mark nested protos
|
||||
if (f->p[i])
|
||||
markobject(g, f->p[i]);
|
||||
}
|
||||
for (i = 0; i < f->sizelocvars; i++)
|
||||
{ /* mark local-variable names */
|
||||
{ // mark local-variable names
|
||||
if (f->locvars[i].varname)
|
||||
stringmark(f->locvars[i].varname);
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ static void traverseclosure(global_State* g, Closure* cl)
|
|||
if (cl->isC)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
else
|
||||
|
@ -284,7 +284,7 @@ static void traverseclosure(global_State* g, Closure* cl)
|
|||
int i;
|
||||
LUAU_ASSERT(cl->nupvalues == cl->l.p->nups);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
@ -296,11 +296,11 @@ static void traversestack(global_State* g, lua_State* l, bool clearstack)
|
|||
stringmark(l->namecall);
|
||||
for (StkId o = l->stack; o < l->top; o++)
|
||||
markvalue(g, o);
|
||||
/* final traversal? */
|
||||
// final traversal?
|
||||
if (g->gcstate == GCSatomic || clearstack)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -320,8 +320,8 @@ static size_t propagatemark(global_State* g)
|
|||
{
|
||||
Table* h = gco2h(o);
|
||||
g->gray = h->gclist;
|
||||
if (traversetable(g, h)) /* table is weak? */
|
||||
black2gray(o); /* keep it gray */
|
||||
if (traversetable(g, h)) // table is weak?
|
||||
black2gray(o); // keep it gray
|
||||
return sizeof(Table) + sizeof(TValue) * h->sizearray + sizeof(LuaNode) * sizenode(h);
|
||||
}
|
||||
case LUA_TFUNCTION:
|
||||
|
@ -393,7 +393,7 @@ static int isobjcleared(GCObject* o)
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -417,8 +417,8 @@ static size_t cleartable(lua_State* L, GCObject* l)
|
|||
while (i--)
|
||||
{
|
||||
TValue* o = &h->array[i];
|
||||
if (iscleared(o)) /* value was collected? */
|
||||
setnilvalue(o); /* remove value */
|
||||
if (iscleared(o)) // value was collected?
|
||||
setnilvalue(o); // remove value
|
||||
}
|
||||
i = sizenode(h);
|
||||
int activevalues = 0;
|
||||
|
@ -432,8 +432,8 @@ static size_t cleartable(lua_State* L, GCObject* l)
|
|||
// can we clear key or value?
|
||||
if (iscleared(gkey(n)) || iscleared(gval(n)))
|
||||
{
|
||||
setnilvalue(gval(n)); /* remove value ... */
|
||||
removeentry(n); /* remove entry from table */
|
||||
setnilvalue(gval(n)); // remove value ...
|
||||
removeentry(n); // remove entry from table
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -460,7 +460,7 @@ static size_t cleartable(lua_State* L, GCObject* 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;
|
||||
for (CallInfo* ci = L->base_ci; ci <= L->ci; ci++)
|
||||
{
|
||||
|
@ -469,16 +469,16 @@ static void shrinkstack(lua_State* L)
|
|||
lim = ci->top;
|
||||
}
|
||||
|
||||
/* 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 s_used = cast_int(lim - L->stack); /* part of stack in use */
|
||||
if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */
|
||||
return; /* do not touch the stacks */
|
||||
// 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 s_used = cast_int(lim - L->stack); // part of stack in use
|
||||
if (L->size_ci > LUAI_MAXCALLS) // handling overflow?
|
||||
return; // do not touch the stacks
|
||||
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));
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -516,20 +516,20 @@ static void freeobj(lua_State* L, GCObject* o, lua_Page* page)
|
|||
static void shrinkbuffers(lua_State* L)
|
||||
{
|
||||
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)
|
||||
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)
|
||||
{
|
||||
global_State* g = L->global;
|
||||
/* check size of string hash */
|
||||
// check size of string hash
|
||||
int hashsize = g->strt.size;
|
||||
while (g->strt.nuse < cast_to(uint32_t, hashsize / 4) && hashsize > LUA_MINSTRTABSIZE * 2)
|
||||
hashsize /= 2;
|
||||
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)
|
||||
|
@ -562,7 +562,7 @@ void luaC_freeall(lua_State* L)
|
|||
|
||||
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(L->global->strt.nuse == 0);
|
||||
|
@ -577,7 +577,7 @@ static void markmt(global_State* g)
|
|||
markobject(g, g->mt[i]);
|
||||
}
|
||||
|
||||
/* mark root set */
|
||||
// mark root set
|
||||
static void markroot(lua_State* L)
|
||||
{
|
||||
global_State* g = L->global;
|
||||
|
@ -585,7 +585,7 @@ static void markroot(lua_State* L)
|
|||
g->grayagain = NULL;
|
||||
g->weak = NULL;
|
||||
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);
|
||||
markvalue(g, registry(L));
|
||||
markmt(g);
|
||||
|
@ -616,28 +616,28 @@ static size_t atomic(lua_State* L)
|
|||
double currts = lua_clock();
|
||||
#endif
|
||||
|
||||
/* remark occasional upvalues of (maybe) dead threads */
|
||||
// remark occasional upvalues of (maybe) dead threads
|
||||
work += remarkupvals(g);
|
||||
/* traverse objects caught by write barrier and by 'remarkupvals' */
|
||||
// traverse objects caught by write barrier and by 'remarkupvals'
|
||||
work += propagateall(g);
|
||||
|
||||
#ifdef LUAI_GCMETRICS
|
||||
g->gcmetrics.currcycle.atomictimeupval += recordGcDeltaTime(currts);
|
||||
#endif
|
||||
|
||||
/* remark weak tables */
|
||||
// remark weak tables
|
||||
g->gray = g->weak;
|
||||
g->weak = NULL;
|
||||
LUAU_ASSERT(!iswhite(obj2gco(g->mainthread)));
|
||||
markobject(g, L); /* mark running thread */
|
||||
markmt(g); /* mark basic metatables (again) */
|
||||
markobject(g, L); // mark running thread
|
||||
markmt(g); // mark basic metatables (again)
|
||||
work += propagateall(g);
|
||||
|
||||
#ifdef LUAI_GCMETRICS
|
||||
g->gcmetrics.currcycle.atomictimeweak += recordGcDeltaTime(currts);
|
||||
#endif
|
||||
|
||||
/* remark gray again */
|
||||
// remark gray again
|
||||
g->gray = g->grayagain;
|
||||
g->grayagain = NULL;
|
||||
work += propagateall(g);
|
||||
|
@ -646,7 +646,7 @@ static size_t atomic(lua_State* L)
|
|||
g->gcmetrics.currcycle.atomictimegray += recordGcDeltaTime(currts);
|
||||
#endif
|
||||
|
||||
/* remove collected objects from weak tables */
|
||||
// remove collected objects from weak tables
|
||||
work += cleartable(L, g->weak);
|
||||
g->weak = NULL;
|
||||
|
||||
|
@ -654,7 +654,7 @@ static size_t atomic(lua_State* L)
|
|||
g->gcmetrics.currcycle.atomictimeclear += recordGcDeltaTime(currts);
|
||||
#endif
|
||||
|
||||
/* flip current white */
|
||||
// flip current white
|
||||
g->currentwhite = cast_byte(otherwhite(g));
|
||||
g->sweepgcopage = g->allgcopages;
|
||||
g->gcstate = GCSsweep;
|
||||
|
@ -733,7 +733,7 @@ static size_t gcstep(lua_State* L, size_t limit)
|
|||
{
|
||||
case GCSpause:
|
||||
{
|
||||
markroot(L); /* start a new collection */
|
||||
markroot(L); // start a new collection
|
||||
LUAU_ASSERT(g->gcstate == GCSpropagate);
|
||||
break;
|
||||
}
|
||||
|
@ -765,7 +765,7 @@ static size_t gcstep(lua_State* L, size_t limit)
|
|||
cost += propagatemark(g);
|
||||
}
|
||||
|
||||
if (!g->gray) /* no more `gray' objects */
|
||||
if (!g->gray) // no more `gray' objects
|
||||
{
|
||||
#ifdef LUAI_GCMETRICS
|
||||
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.atomicstarttotalsizebytes = g->totalbytes;
|
||||
|
||||
cost = atomic(L); /* finish mark phase */
|
||||
cost = atomic(L); // finish mark phase
|
||||
|
||||
LUAU_ASSERT(g->gcstate == GCSsweep);
|
||||
break;
|
||||
|
@ -810,7 +810,7 @@ static size_t gcstep(lua_State* L, size_t limit)
|
|||
sweepgco(L, NULL, obj2gco(g->mainthread));
|
||||
|
||||
shrinkbuffers(L);
|
||||
g->gcstate = GCSpause; /* end collection */
|
||||
g->gcstate = GCSpause; // end collection
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -878,7 +878,7 @@ size_t luaC_step(lua_State* L, bool assist)
|
|||
{
|
||||
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);
|
||||
size_t debt = g->totalbytes - g->GCthreshold;
|
||||
|
||||
|
@ -947,16 +947,16 @@ void luaC_fullgc(lua_State* L)
|
|||
|
||||
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;
|
||||
/* reset other collector lists */
|
||||
// reset other collector lists
|
||||
g->gray = NULL;
|
||||
g->grayagain = NULL;
|
||||
g->weak = NULL;
|
||||
g->gcstate = GCSsweep;
|
||||
}
|
||||
LUAU_ASSERT(g->gcstate == GCSsweep);
|
||||
/* finish any pending sweep phase */
|
||||
// finish any pending sweep phase
|
||||
while (g->gcstate != GCSpause)
|
||||
{
|
||||
LUAU_ASSERT(g->gcstate == GCSsweep);
|
||||
|
@ -968,13 +968,13 @@ void luaC_fullgc(lua_State* L)
|
|||
startGcCycleMetrics(g);
|
||||
#endif
|
||||
|
||||
/* run a full collection cycle */
|
||||
// run a full collection cycle
|
||||
markroot(L);
|
||||
while (g->gcstate != GCSpause)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
LUAU_ASSERT(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
|
||||
LUAU_ASSERT(g->gcstate != GCSpause);
|
||||
/* must keep invariant? */
|
||||
// must keep invariant?
|
||||
if (keepinvariant(g))
|
||||
reallymarkobject(g, v); /* restore invariant */
|
||||
else /* don't mind */
|
||||
makewhite(g, o); /* mark as white just to avoid other barriers */
|
||||
reallymarkobject(g, v); // restore invariant
|
||||
else // don't mind
|
||||
makewhite(g, o); // mark as white just to avoid other barriers
|
||||
}
|
||||
|
||||
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(g->gcstate != GCSpause);
|
||||
black2gray(o); /* make table gray (again) */
|
||||
black2gray(o); // make table gray (again)
|
||||
t->gclist = g->grayagain;
|
||||
g->grayagain = o;
|
||||
}
|
||||
|
@ -1044,7 +1044,7 @@ void luaC_barrierback(lua_State* L, Table* t)
|
|||
GCObject* o = obj2gco(t);
|
||||
LUAU_ASSERT(isblack(o) && !isdead(g, o));
|
||||
LUAU_ASSERT(g->gcstate != GCSpause);
|
||||
black2gray(o); /* make table gray (again) */
|
||||
black2gray(o); // make table gray (again)
|
||||
t->gclist = g->grayagain;
|
||||
g->grayagain = o;
|
||||
}
|
||||
|
@ -1066,11 +1066,11 @@ void luaC_initupval(lua_State* L, UpVal* uv)
|
|||
{
|
||||
if (keepinvariant(g))
|
||||
{
|
||||
gray2black(o); /* closed upvalues need barrier */
|
||||
gray2black(o); // closed upvalues need barrier
|
||||
luaC_barrier(L, uv, uv->v);
|
||||
}
|
||||
else
|
||||
{ /* sweep phase: sweep it (turning it into white) */
|
||||
{ // sweep phase: sweep it (turning it into white)
|
||||
makewhite(g, o);
|
||||
LUAU_ASSERT(g->gcstate != GCSpause);
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
/*
|
||||
** Default settings for GC tunables (settable via lua_gc)
|
||||
*/
|
||||
#define LUAI_GCGOAL 200 /* 200% (allow heap to double compared to live heap size) */
|
||||
#define LUAI_GCSTEPMUL 200 /* GC runs 'twice the speed' of memory allocation */
|
||||
#define LUAI_GCSTEPSIZE 1 /* GC runs every KB of memory allocation */
|
||||
#define LUAI_GCGOAL 200 // 200% (allow heap to double compared to live heap size)
|
||||
#define LUAI_GCSTEPMUL 200 // GC runs 'twice the speed' of memory allocation
|
||||
#define LUAI_GCSTEPSIZE 1 // GC runs every KB of memory allocation
|
||||
|
||||
/*
|
||||
** Possible states of the Garbage Collector
|
||||
|
|
|
@ -19,7 +19,7 @@ static void validateobjref(global_State* g, GCObject* f, GCObject* t)
|
|||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ static void validateproto(global_State* g, Proto* f)
|
|||
|
||||
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))
|
||||
{
|
||||
LUAU_ASSERT(g->gcstate == GCSsweep);
|
||||
|
|
|
@ -195,7 +195,7 @@ static int math_ldexp(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);
|
||||
int 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)
|
||||
{
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int n = lua_gettop(L); // number of arguments
|
||||
double dmax = luaL_checknumber(L, 1);
|
||||
int i;
|
||||
for (i = 2; i <= n; i++)
|
||||
|
@ -227,29 +227,29 @@ static int math_random(lua_State* L)
|
|||
{
|
||||
global_State* g = L->global;
|
||||
switch (lua_gettop(L))
|
||||
{ /* check number of arguments */
|
||||
{ // check number of arguments
|
||||
case 0:
|
||||
{ /* no arguments */
|
||||
{ // no arguments
|
||||
// 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.
|
||||
uint32_t rl = pcg32_random(&g->rngstate);
|
||||
uint32_t rh = pcg32_random(&g->rngstate);
|
||||
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;
|
||||
}
|
||||
case 1:
|
||||
{ /* only upper limit */
|
||||
{ // only upper limit
|
||||
int u = luaL_checkinteger(L, 1);
|
||||
luaL_argcheck(L, 1 <= u, 1, "interval is empty");
|
||||
|
||||
uint64_t x = uint64_t(u) * pcg32_random(&g->rngstate);
|
||||
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;
|
||||
}
|
||||
case 2:
|
||||
{ /* lower and upper limits */
|
||||
{ // lower and upper limits
|
||||
int l = luaL_checkinteger(L, 1);
|
||||
int u = luaL_checkinteger(L, 2);
|
||||
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
|
||||
uint64_t x = uint64_t(ul + 1) * pcg32_random(&g->rngstate);
|
||||
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;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -42,7 +42,7 @@ LUAU_FASTMATH_END
|
|||
|
||||
#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)
|
||||
#define luai_num2unsigned(i, n) \
|
||||
{ \
|
||||
|
|
|
@ -48,7 +48,7 @@ int luaO_rawequalObj(const TValue* t1, const TValue* t2)
|
|||
case LUA_TVECTOR:
|
||||
return luai_veceq(vvalue(t1), vvalue(t2));
|
||||
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:
|
||||
return pvalue(t1) == pvalue(t2);
|
||||
default:
|
||||
|
@ -71,7 +71,7 @@ int luaO_rawequalKey(const TKey* t1, const TValue* t2)
|
|||
case LUA_TVECTOR:
|
||||
return luai_veceq(vvalue(t1), vvalue(t2));
|
||||
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:
|
||||
return pvalue(t1) == pvalue(t2);
|
||||
default:
|
||||
|
@ -85,15 +85,15 @@ int luaO_str2d(const char* s, double* result)
|
|||
char* endptr;
|
||||
*result = luai_str2num(s, &endptr);
|
||||
if (endptr == s)
|
||||
return 0; /* conversion failed */
|
||||
if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */
|
||||
return 0; // conversion failed
|
||||
if (*endptr == 'x' || *endptr == 'X') // maybe an hexadecimal constant?
|
||||
*result = cast_num(strtoul(s, &endptr, 16));
|
||||
if (*endptr == '\0')
|
||||
return 1; /* most common case */
|
||||
return 1; // most common case
|
||||
while (isspace(cast_to(unsigned char, *endptr)))
|
||||
endptr++;
|
||||
if (*endptr != '\0')
|
||||
return 0; /* invalid trailing characters? */
|
||||
return 0; // invalid trailing characters?
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ void luaO_chunkid(char* out, const char* source, size_t bufflen)
|
|||
{
|
||||
if (*source == '=')
|
||||
{
|
||||
source++; /* skip the `=' */
|
||||
source++; // skip the `='
|
||||
size_t srclen = strlen(source);
|
||||
size_t dstlen = srclen < bufflen ? srclen : bufflen - 1;
|
||||
memcpy(out, source, dstlen);
|
||||
|
@ -130,26 +130,26 @@ void luaO_chunkid(char* out, const char* source, size_t bufflen)
|
|||
else if (*source == '@')
|
||||
{
|
||||
size_t l;
|
||||
source++; /* skip the `@' */
|
||||
source++; // skip the `@'
|
||||
bufflen -= sizeof("...");
|
||||
l = strlen(source);
|
||||
strcpy(out, "");
|
||||
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, source);
|
||||
}
|
||||
else
|
||||
{ /* out = [string "string"] */
|
||||
size_t len = strcspn(source, "\n\r"); /* stop at first newline */
|
||||
{ // out = [string "string"]
|
||||
size_t len = strcspn(source, "\n\r"); // stop at first newline
|
||||
bufflen -= sizeof("[string \"...\"]");
|
||||
if (len > bufflen)
|
||||
len = bufflen;
|
||||
strcpy(out, "[string \"");
|
||||
if (source[len] != '\0')
|
||||
{ /* must truncate? */
|
||||
{ // must truncate?
|
||||
strncat(out, source, len);
|
||||
strcat(out, "...");
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ typedef struct lua_TValue
|
|||
int tt;
|
||||
} TValue;
|
||||
|
||||
/* Macros to test type */
|
||||
// Macros to test type
|
||||
#define ttisnil(o) (ttype(o) == LUA_TNIL)
|
||||
#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
|
||||
#define ttisstring(o) (ttype(o) == LUA_TSTRING)
|
||||
|
@ -62,7 +62,7 @@ typedef struct lua_TValue
|
|||
#define ttisvector(o) (ttype(o) == LUA_TVECTOR)
|
||||
#define ttisupval(o) (ttype(o) == LUA_TUPVAL)
|
||||
|
||||
/* Macros to access values */
|
||||
// Macros to access values
|
||||
#define ttype(o) ((o)->tt)
|
||||
#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
|
||||
#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)))
|
||||
|
||||
/* Macros to set values */
|
||||
// Macros to set values
|
||||
#define setnilvalue(obj) ((obj)->tt = LUA_TNIL)
|
||||
|
||||
#define setnvalue(obj, x) \
|
||||
|
@ -200,18 +200,18 @@ typedef struct lua_TValue
|
|||
** different types of sets, according to destination
|
||||
*/
|
||||
|
||||
/* from stack to (same) stack */
|
||||
// from stack to (same) stack
|
||||
#define setobjs2s setobj
|
||||
/* to stack (not from same stack) */
|
||||
// to stack (not from same stack)
|
||||
#define setobj2s setobj
|
||||
#define setsvalue2s setsvalue
|
||||
#define sethvalue2s sethvalue
|
||||
#define setptvalue2s setptvalue
|
||||
/* from table to same table */
|
||||
// from table to same table
|
||||
#define setobjt2t setobj
|
||||
/* to table */
|
||||
// to table
|
||||
#define setobj2t setobj
|
||||
/* to new object */
|
||||
// to new object
|
||||
#define setobj2n setobj
|
||||
#define setsvalue2n setsvalue
|
||||
|
||||
|
@ -219,7 +219,7 @@ typedef struct lua_TValue
|
|||
|
||||
#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
|
||||
|
@ -269,13 +269,13 @@ typedef struct Proto
|
|||
CommonHeader;
|
||||
|
||||
|
||||
TValue* k; /* constants used by the function */
|
||||
Instruction* code; /* function bytecode */
|
||||
struct Proto** p; /* functions defined inside the function */
|
||||
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 */
|
||||
struct LocVar* locvars; /* information about local variables */
|
||||
TString** upvalues; /* upvalue names */
|
||||
TValue* k; // constants used by the function
|
||||
Instruction* code; // function bytecode
|
||||
struct Proto** p; // functions defined inside the function
|
||||
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
|
||||
struct LocVar* locvars; // information about local variables
|
||||
TString** upvalues; // upvalue names
|
||||
TString* source;
|
||||
|
||||
TString* debugname;
|
||||
|
@ -294,7 +294,7 @@ typedef struct Proto
|
|||
int linedefined;
|
||||
|
||||
|
||||
uint8_t nups; /* number of upvalues */
|
||||
uint8_t nups; // number of upvalues
|
||||
uint8_t numparams;
|
||||
uint8_t is_vararg;
|
||||
uint8_t maxstacksize;
|
||||
|
@ -304,9 +304,9 @@ typedef struct Proto
|
|||
typedef struct LocVar
|
||||
{
|
||||
TString* varname;
|
||||
int startpc; /* first point where variable is active */
|
||||
int endpc; /* first point where variable is dead */
|
||||
uint8_t reg; /* register slot, relative to base, where variable is stored */
|
||||
int startpc; // first point where variable is active
|
||||
int endpc; // first point where variable is dead
|
||||
uint8_t reg; // register slot, relative to base, where variable is stored
|
||||
} LocVar;
|
||||
|
||||
/*
|
||||
|
@ -317,19 +317,19 @@ typedef struct UpVal
|
|||
{
|
||||
CommonHeader;
|
||||
// 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
|
||||
{
|
||||
TValue value; /* the value (when closed) */
|
||||
TValue value; // the value (when closed)
|
||||
struct
|
||||
{
|
||||
/* global double linked list (when open) */
|
||||
// global double linked list (when open)
|
||||
struct UpVal* prev;
|
||||
struct UpVal* next;
|
||||
|
||||
/* thread double linked list (when open) */
|
||||
// thread double linked list (when open)
|
||||
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;
|
||||
} l;
|
||||
} u;
|
||||
|
@ -381,7 +381,7 @@ typedef struct TKey
|
|||
::Value value;
|
||||
int extra[LUA_EXTRA_SIZE];
|
||||
unsigned tt : 4;
|
||||
int next : 28; /* for chaining */
|
||||
int next : 28; // for chaining
|
||||
} TKey;
|
||||
|
||||
typedef struct LuaNode
|
||||
|
@ -390,7 +390,7 @@ typedef struct LuaNode
|
|||
TKey key;
|
||||
} LuaNode;
|
||||
|
||||
/* copy a value into a key */
|
||||
// copy a value into a key
|
||||
#define setnodekey(L, node, obj) \
|
||||
{ \
|
||||
LuaNode* n_ = (node); \
|
||||
|
@ -401,7 +401,7 @@ typedef struct LuaNode
|
|||
checkliveness(L->global, i_o); \
|
||||
}
|
||||
|
||||
/* copy a value from a key */
|
||||
// copy a value from a key
|
||||
#define getnodekey(L, obj, node) \
|
||||
{ \
|
||||
TValue* i_o = (obj); \
|
||||
|
@ -418,22 +418,22 @@ typedef struct Table
|
|||
CommonHeader;
|
||||
|
||||
|
||||
uint8_t tmcache; /* 1<<p means tagmethod(p) is not present */
|
||||
uint8_t readonly; /* sandboxing feature to prohibit writes to table */
|
||||
uint8_t safeenv; /* environment doesn't share globals with other scripts */
|
||||
uint8_t lsizenode; /* log2 of size of `node' array */
|
||||
uint8_t nodemask8; /* (1<<lsizenode)-1, truncated to 8 bits */
|
||||
uint8_t tmcache; // 1<<p means tagmethod(p) is not present
|
||||
uint8_t readonly; // sandboxing feature to prohibit writes to table
|
||||
uint8_t safeenv; // environment doesn't share globals with other scripts
|
||||
uint8_t lsizenode; // log2 of size of `node' array
|
||||
uint8_t nodemask8; // (1<<lsizenode)-1, truncated to 8 bits
|
||||
|
||||
int sizearray; /* size of `array' array */
|
||||
int sizearray; // size of `array' array
|
||||
union
|
||||
{
|
||||
int lastfree; /* any free position is before this position */
|
||||
int aboundary; /* negated 'boundary' of `array' array; iff aboundary < 0 */
|
||||
int lastfree; // any free position is before this position
|
||||
int aboundary; // negated 'boundary' of `array' array; iff aboundary < 0
|
||||
};
|
||||
|
||||
|
||||
struct Table* metatable;
|
||||
TValue* array; /* array part */
|
||||
TValue* array; // array part
|
||||
LuaNode* node;
|
||||
GCObject* gclist;
|
||||
} Table;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (value < 0) /* undefined? */
|
||||
return; /* does not set field */
|
||||
if (value < 0) // undefined?
|
||||
return; // does not set field
|
||||
lua_pushboolean(L, value);
|
||||
lua_setfield(L, -2, key);
|
||||
}
|
||||
|
@ -85,9 +85,9 @@ static int os_date(lua_State* L)
|
|||
struct tm tm;
|
||||
struct tm* stm;
|
||||
if (*s == '!')
|
||||
{ /* UTC? */
|
||||
{ // UTC?
|
||||
stm = gmtime_r(&t, &tm);
|
||||
s++; /* skip `!' */
|
||||
s++; // skip `!'
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -95,13 +95,13 @@ static int os_date(lua_State* L)
|
|||
stm = t < 0 ? NULL : localtime_r(&t, &tm);
|
||||
}
|
||||
|
||||
if (stm == NULL) /* invalid date? */
|
||||
if (stm == NULL) // invalid date?
|
||||
{
|
||||
lua_pushnil(L);
|
||||
}
|
||||
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, "min", stm->tm_min);
|
||||
setfield(L, "hour", stm->tm_hour);
|
||||
|
@ -122,7 +122,7 @@ static int os_date(lua_State* L)
|
|||
luaL_buffinit(L, &b);
|
||||
for (; *s; s++)
|
||||
{
|
||||
if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */
|
||||
if (*s != '%' || *(s + 1) == '\0') // no conversion specifier?
|
||||
{
|
||||
luaL_addchar(&b, *s);
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ static int os_date(lua_State* L)
|
|||
else
|
||||
{
|
||||
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);
|
||||
reslen = strftime(buff, sizeof(buff), cc, stm);
|
||||
luaL_addlstring(&b, buff, reslen);
|
||||
|
@ -147,13 +147,13 @@ static int os_date(lua_State* L)
|
|||
static int os_time(lua_State* L)
|
||||
{
|
||||
time_t t;
|
||||
if (lua_isnoneornil(L, 1)) /* called without args? */
|
||||
t = time(NULL); /* get current time */
|
||||
if (lua_isnoneornil(L, 1)) // called without args?
|
||||
t = time(NULL); // get current time
|
||||
else
|
||||
{
|
||||
struct tm ts;
|
||||
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_min = getfield(L, "min", 0);
|
||||
ts.tm_hour = getfield(L, "hour", 12);
|
||||
|
|
|
@ -21,22 +21,22 @@ typedef struct LG
|
|||
|
||||
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->ci = L1->base_ci;
|
||||
L1->size_ci = BASIC_CI_SIZE;
|
||||
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->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
|
||||
TValue* stack = L1->stack;
|
||||
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->stack_last = stack + (L1->stacksize - EXTRA_STACK);
|
||||
/* initialize first ci */
|
||||
// initialize first ci
|
||||
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->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)
|
||||
{
|
||||
global_State* g = L->global;
|
||||
stack_init(L, L); /* init stack */
|
||||
L->gt = luaH_new(L, 0, 2); /* table of globals */
|
||||
sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */
|
||||
luaS_resize(L, LUA_MINSTRTABSIZE); /* initial size of string table */
|
||||
stack_init(L, L); // init stack
|
||||
L->gt = luaH_new(L, 0, 2); // table of globals
|
||||
sethvalue(L, registry(L), luaH_new(L, 0, 2)); // registry
|
||||
luaS_resize(L, LUA_MINSTRTABSIZE); // initial size of string table
|
||||
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_ERRERRMSG)); /* 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
|
||||
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)
|
||||
{
|
||||
global_State* g = L->global;
|
||||
luaF_close(L, L->stack); /* close all upvalues for this thread */
|
||||
luaC_freeall(L); /* collect all objects */
|
||||
luaF_close(L, L->stack); // close all upvalues for this thread
|
||||
luaC_freeall(L); // collect all objects
|
||||
LUAU_ASSERT(g->strbufgc == NULL);
|
||||
LUAU_ASSERT(g->strt.nuse == 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);
|
||||
preinit_state(L1, L->global);
|
||||
L1->activememcat = L->activememcat; // inherit the active memory category
|
||||
stack_init(L1, L); /* init stack */
|
||||
L1->gt = L->gt; /* share table of globals */
|
||||
stack_init(L1, L); // init stack
|
||||
L1->gt = L->gt; // share table of globals
|
||||
L1->singlestep = L->singlestep;
|
||||
LUAU_ASSERT(iswhite(obj2gco(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)
|
||||
{
|
||||
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);
|
||||
global_State* g = L->global;
|
||||
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)
|
||||
{
|
||||
/* close upvalues before clearing anything */
|
||||
// close upvalues before clearing anything
|
||||
luaF_close(L, L->stack);
|
||||
/* clear call frames */
|
||||
// clear call frames
|
||||
CallInfo* ci = L->base_ci;
|
||||
ci->func = L->stack;
|
||||
ci->base = ci->func + 1;
|
||||
|
@ -141,12 +141,12 @@ void lua_resetthread(lua_State* L)
|
|||
L->ci = ci;
|
||||
if (L->size_ci != BASIC_CI_SIZE)
|
||||
luaD_reallocCI(L, BASIC_CI_SIZE);
|
||||
/* clear thread state */
|
||||
// clear thread state
|
||||
L->status = LUA_OK;
|
||||
L->base = L->ci->base;
|
||||
L->top = L->ci->base;
|
||||
L->nCcalls = L->baseCcalls = 0;
|
||||
/* clear thread stack */
|
||||
// clear thread stack
|
||||
if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK)
|
||||
luaD_reallocstack(L, BASIC_STACK_SIZE);
|
||||
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->uvhead.u.l.prev = &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->errorjmp = NULL;
|
||||
g->rngstate = 0;
|
||||
|
@ -224,7 +224,7 @@ lua_State* lua_newstate(lua_Alloc f, void* ud)
|
|||
|
||||
if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0)
|
||||
{
|
||||
/* memory allocation error: free partial state */
|
||||
// memory allocation error: free partial state
|
||||
close_state(L);
|
||||
L = NULL;
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ lua_State* lua_newstate(lua_Alloc f, void* ud)
|
|||
|
||||
void lua_close(lua_State* L)
|
||||
{
|
||||
L = L->global->mainthread; /* only the main thread can be closed */
|
||||
luaF_close(L, L->stack); /* close all upvalues for this thread */
|
||||
L = L->global->mainthread; // only the main thread can be closed
|
||||
luaF_close(L, L->stack); // close all upvalues for this thread
|
||||
close_state(L);
|
||||
}
|
||||
|
|
100
VM/src/lstate.h
100
VM/src/lstate.h
|
@ -5,10 +5,10 @@
|
|||
#include "lobject.h"
|
||||
#include "ltm.h"
|
||||
|
||||
/* registry */
|
||||
// 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 BASIC_CI_SIZE 8
|
||||
|
@ -20,7 +20,7 @@ typedef struct stringtable
|
|||
{
|
||||
|
||||
TString** hash;
|
||||
uint32_t nuse; /* number of elements */
|
||||
uint32_t nuse; // number of elements
|
||||
int size;
|
||||
} stringtable;
|
||||
// clang-format on
|
||||
|
@ -57,18 +57,18 @@ typedef struct stringtable
|
|||
typedef struct CallInfo
|
||||
{
|
||||
|
||||
StkId base; /* base for this function */
|
||||
StkId func; /* function index in the stack */
|
||||
StkId top; /* top for this function */
|
||||
StkId base; // base for this function
|
||||
StkId func; // function index in the stack
|
||||
StkId top; // top for this function
|
||||
const Instruction* savedpc;
|
||||
|
||||
int nresults; /* expected number of results from this function */
|
||||
unsigned int flags; /* call frame flags, see LUA_CALLINFO_* */
|
||||
int nresults; // expected number of results from this function
|
||||
unsigned int flags; // call frame flags, see LUA_CALLINFO_*
|
||||
} CallInfo;
|
||||
// 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_HANDLE (1 << 1) /* should the error thrown during execution get handled by continuation from this callinfo? func must be C */
|
||||
#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 curr_func(L) (clvalue(L->ci->func))
|
||||
#define ci_func(ci) (clvalue((ci)->func))
|
||||
|
@ -152,55 +152,55 @@ struct GCMetrics
|
|||
// clang-format off
|
||||
typedef struct global_State
|
||||
{
|
||||
stringtable strt; /* hash table for strings */
|
||||
stringtable strt; // hash table for strings
|
||||
|
||||
|
||||
lua_Alloc frealloc; /* function to reallocate memory */
|
||||
void* ud; /* auxiliary data to `frealloc' */
|
||||
lua_Alloc frealloc; // function to reallocate memory
|
||||
void* ud; // auxiliary data to `frealloc'
|
||||
|
||||
|
||||
uint8_t currentwhite;
|
||||
uint8_t gcstate; /* state of garbage collector */
|
||||
uint8_t gcstate; // state of garbage collector
|
||||
|
||||
|
||||
GCObject* gray; /* list of gray objects */
|
||||
GCObject* grayagain; /* list of objects to be traversed atomically */
|
||||
GCObject* weak; /* list of weak tables (to be cleared) */
|
||||
GCObject* gray; // list of gray objects
|
||||
GCObject* grayagain; // list of objects to be traversed atomically
|
||||
GCObject* weak; // list of weak tables (to be cleared)
|
||||
|
||||
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
|
||||
int gcgoal; // see LUAI_GCGOAL
|
||||
int gcstepmul; // see LUAI_GCSTEPMUL
|
||||
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* 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* 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;
|
||||
UpVal uvhead; /* head of double-linked list of all open upvalues */
|
||||
struct Table* mt[LUA_T_COUNT]; /* metatables for basic types */
|
||||
TString* ttname[LUA_T_COUNT]; /* names for basic types */
|
||||
TString* tmname[TM_N]; /* array with tag-method names */
|
||||
UpVal uvhead; // head of double-linked list of all open upvalues
|
||||
struct Table* mt[LUA_T_COUNT]; // metatables for basic types
|
||||
TString* ttname[LUA_T_COUNT]; // names for basic types
|
||||
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 */
|
||||
int registryfree; /* next free slot in registry */
|
||||
TValue registry; // registry table, used by lua_ref and LUA_REGISTRYINDEX
|
||||
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 ptrenckey[4]; /* pointer encoding key for display */
|
||||
uint64_t rngstate; // PCG random number generator state
|
||||
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;
|
||||
|
||||
|
@ -221,39 +221,39 @@ struct lua_State
|
|||
CommonHeader;
|
||||
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;
|
||||
|
||||
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 base; /* base of current function */
|
||||
StkId top; // first free slot in the stack
|
||||
StkId base; // base of current function
|
||||
global_State* global;
|
||||
CallInfo* ci; /* call info for current function */
|
||||
StkId stack_last; /* last free slot in the stack */
|
||||
StkId stack; /* stack base */
|
||||
CallInfo* ci; // call info for current function
|
||||
StkId stack_last; // last free slot in the stack
|
||||
StkId stack; // stack base
|
||||
|
||||
|
||||
CallInfo* end_ci; /* points after end of ci array*/
|
||||
CallInfo* base_ci; /* array of CallInfo's */
|
||||
CallInfo* end_ci; // points after end of ci array
|
||||
CallInfo* base_ci; // array of CallInfo's
|
||||
|
||||
|
||||
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 baseCcalls; /* nested C calls when resuming coroutine */
|
||||
unsigned short nCcalls; // number of nested C calls
|
||||
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 */
|
||||
UpVal* openupval; /* list of open upvalues in this stack */
|
||||
Table* gt; // table of globals
|
||||
UpVal* openupval; // list of open upvalues in this stack
|
||||
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;
|
||||
};
|
||||
|
@ -271,10 +271,10 @@ union GCObject
|
|||
struct Table h;
|
||||
struct Proto p;
|
||||
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 gco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
|
||||
#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 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))
|
||||
|
||||
LUAI_FUNC lua_State* luaE_newthread(lua_State* L);
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauLazyAtoms, false)
|
||||
|
||||
unsigned int luaS_hash(const char* str, size_t len)
|
||||
{
|
||||
// Note that this hashing algorithm is replicated in BytecodeBuilder.cpp, BytecodeBuilder::getStringHash
|
||||
|
@ -50,17 +48,17 @@ void luaS_resize(lua_State* L, int newsize)
|
|||
stringtable* tb = &L->global->strt;
|
||||
for (int i = 0; i < newsize; i++)
|
||||
newhash[i] = NULL;
|
||||
/* rehash */
|
||||
// rehash
|
||||
for (int i = 0; i < tb->size; i++)
|
||||
{
|
||||
TString* p = tb->hash[i];
|
||||
while (p)
|
||||
{ /* for each node in the list */
|
||||
TString* next = p->next; /* save next */
|
||||
{ // for each node in the list
|
||||
TString* next = p->next; // save next
|
||||
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));
|
||||
p->next = newhash[h1]; /* chain it */
|
||||
p->next = newhash[h1]; // chain it
|
||||
newhash[h1] = p;
|
||||
p = next;
|
||||
}
|
||||
|
@ -83,15 +81,15 @@ static TString* newlstr(lua_State* L, const char* str, size_t l, unsigned int h)
|
|||
ts->tt = LUA_TSTRING;
|
||||
ts->memcat = L->activememcat;
|
||||
memcpy(ts->data, str, l);
|
||||
ts->data[l] = '\0'; /* ending 0 */
|
||||
ts->atom = FFlag::LuauLazyAtoms ? ATOM_UNDEF : L->global->cb.useratom ? L->global->cb.useratom(ts->data, l) : -1;
|
||||
ts->data[l] = '\0'; // ending 0
|
||||
ts->atom = ATOM_UNDEF;
|
||||
tb = &L->global->strt;
|
||||
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->nuse++;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -165,9 +163,7 @@ TString* luaS_buffinish(lua_State* L, TString* ts)
|
|||
|
||||
ts->hash = h;
|
||||
ts->data[ts->len] = '\0'; // ending 0
|
||||
|
||||
// Complete string object
|
||||
ts->atom = FFlag::LuauLazyAtoms ? ATOM_UNDEF : L->global->cb.useratom ? L->global->cb.useratom(ts->data, ts->len) : -1;
|
||||
ts->atom = ATOM_UNDEF;
|
||||
ts->next = tb->hash[bucket]; // chain new entry
|
||||
tb->hash[bucket] = ts;
|
||||
|
||||
|
@ -185,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))
|
||||
{
|
||||
/* string may be dead */
|
||||
// string may be dead
|
||||
if (isdead(L->global, obj2gco(el)))
|
||||
changewhite(obj2gco(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)
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
/* string size limit */
|
||||
// string size limit
|
||||
#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 sizestring(len) (offsetof(TString, data) + len + 1)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -44,13 +44,10 @@ static_assert(TKey{{NULL}, {0}, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough
|
|||
static_assert(TKey{{NULL}, {0}, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
|
||||
static_assert(TKey{{NULL}, {0}, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
|
||||
|
||||
// reset cache of absent metamethods, cache is updated in luaT_gettm
|
||||
#define invalidateTMcache(t) t->tmcache = 0
|
||||
|
||||
// empty hash data points to dummynode so that we can always dereference it
|
||||
const LuaNode luaH_dummynode = {
|
||||
{{NULL}, {0}, LUA_TNIL}, /* value */
|
||||
{{NULL}, {0}, LUA_TNIL, 0} /* key */
|
||||
{{NULL}, {0}, LUA_TNIL}, // value
|
||||
{{NULL}, {0}, LUA_TNIL, 0} // key
|
||||
};
|
||||
|
||||
#define dummynode (&luaH_dummynode)
|
||||
|
@ -173,52 +170,52 @@ static int findindex(lua_State* L, Table* t, StkId key)
|
|||
{
|
||||
int i;
|
||||
if (ttisnil(key))
|
||||
return -1; /* first iteration */
|
||||
return -1; // first iteration
|
||||
i = ttisnumber(key) ? arrayindex(nvalue(key)) : -1;
|
||||
if (0 < i && i <= t->sizearray) /* is `key' inside array part? */
|
||||
return i - 1; /* yes; that's the index (corrected to C) */
|
||||
if (0 < i && i <= t->sizearray) // is `key' inside array part?
|
||||
return i - 1; // yes; that's the index (corrected to C)
|
||||
else
|
||||
{
|
||||
LuaNode* n = mainposition(t, key);
|
||||
for (;;)
|
||||
{ /* check whether `key' is somewhere in the chain */
|
||||
/* key may be dead already, but it is ok to use it in `next' */
|
||||
{ // check whether `key' is somewhere in the chain
|
||||
// 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)))
|
||||
{
|
||||
i = cast_int(n - gnode(t, 0)); /* key index in hash table */
|
||||
/* hash elements are numbered after array ones */
|
||||
i = cast_int(n - gnode(t, 0)); // key index in hash table
|
||||
// hash elements are numbered after array ones
|
||||
return i + t->sizearray;
|
||||
}
|
||||
if (gnext(n) == 0)
|
||||
break;
|
||||
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 i = findindex(L, t, key); /* find original element */
|
||||
int i = findindex(L, t, key); // find original element
|
||||
for (i++; i < t->sizearray; i++)
|
||||
{ /* try first array part */
|
||||
{ // try first array part
|
||||
if (!ttisnil(&t->array[i]))
|
||||
{ /* a non-nil value? */
|
||||
{ // a non-nil value?
|
||||
setnvalue(key, cast_num(i + 1));
|
||||
setobj2s(L, key + 1, &t->array[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
for (i -= t->sizearray; i < sizenode(t); i++)
|
||||
{ /* then hash part */
|
||||
{ // then hash part
|
||||
if (!ttisnil(gval(gnode(t, i))))
|
||||
{ /* a non-nil value? */
|
||||
{ // a non-nil value?
|
||||
getnodekey(L, key, gnode(t, i));
|
||||
setobj2s(L, key + 1, gval(gnode(t, i)));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0; /* no more elements */
|
||||
return 0; // no more elements
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -238,23 +235,23 @@ int luaH_next(lua_State* L, Table* t, StkId key)
|
|||
static int computesizes(int nums[], int* narray)
|
||||
{
|
||||
int i;
|
||||
int twotoi; /* 2^i */
|
||||
int a = 0; /* number of elements smaller than 2^i */
|
||||
int na = 0; /* number of elements to go to array part */
|
||||
int n = 0; /* optimal size for array part */
|
||||
int twotoi; // 2^i
|
||||
int a = 0; // number of elements smaller than 2^i
|
||||
int na = 0; // number of elements to go to array part
|
||||
int n = 0; // optimal size for array part
|
||||
for (i = 0, twotoi = 1; twotoi / 2 < *narray; i++, twotoi *= 2)
|
||||
{
|
||||
if (nums[i] > 0)
|
||||
{
|
||||
a += nums[i];
|
||||
if (a > twotoi / 2)
|
||||
{ /* more than half elements present? */
|
||||
n = twotoi; /* optimal size (till now) */
|
||||
na = a; /* all elements smaller than n will go to array part */
|
||||
{ // more than half elements present?
|
||||
n = twotoi; // optimal size (till now)
|
||||
na = a; // all elements smaller than n will go to array part
|
||||
}
|
||||
}
|
||||
if (a == *narray)
|
||||
break; /* all elements already counted */
|
||||
break; // all elements already counted
|
||||
}
|
||||
*narray = n;
|
||||
LUAU_ASSERT(*narray / 2 <= na && na <= *narray);
|
||||
|
@ -265,8 +262,8 @@ static int countint(double key, int* nums)
|
|||
{
|
||||
int k = arrayindex(key);
|
||||
if (0 < k && k <= MAXSIZE)
|
||||
{ /* is `key' an appropriate array index? */
|
||||
nums[ceillog2(k)]++; /* count as such */
|
||||
{ // is `key' an appropriate array index?
|
||||
nums[ceillog2(k)]++; // count as such
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
|
@ -276,20 +273,20 @@ static int countint(double key, int* nums)
|
|||
static int numusearray(const Table* t, int* nums)
|
||||
{
|
||||
int lg;
|
||||
int ttlg; /* 2^lg */
|
||||
int ause = 0; /* summation of `nums' */
|
||||
int i = 1; /* count to traverse all array keys */
|
||||
int ttlg; // 2^lg
|
||||
int ause = 0; // summation of `nums'
|
||||
int i = 1; // count to traverse all array keys
|
||||
for (lg = 0, ttlg = 1; lg <= MAXBITS; lg++, ttlg *= 2)
|
||||
{ /* for each slice */
|
||||
int lc = 0; /* counter */
|
||||
{ // for each slice
|
||||
int lc = 0; // counter
|
||||
int lim = ttlg;
|
||||
if (lim > t->sizearray)
|
||||
{
|
||||
lim = t->sizearray; /* adjust upper limit */
|
||||
lim = t->sizearray; // adjust upper limit
|
||||
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++)
|
||||
{
|
||||
if (!ttisnil(&t->array[i - 1]))
|
||||
|
@ -303,8 +300,8 @@ static int numusearray(const Table* t, int* nums)
|
|||
|
||||
static int numusehash(const Table* t, int* nums, int* pnasize)
|
||||
{
|
||||
int totaluse = 0; /* total number of elements */
|
||||
int ause = 0; /* summation of `nums' */
|
||||
int totaluse = 0; // total number of elements
|
||||
int ause = 0; // summation of `nums'
|
||||
int i = sizenode(t);
|
||||
while (i--)
|
||||
{
|
||||
|
@ -335,8 +332,8 @@ static void setnodevector(lua_State* L, Table* t, int size)
|
|||
{
|
||||
int lsize;
|
||||
if (size == 0)
|
||||
{ /* no elements to hash part? */
|
||||
t->node = cast_to(LuaNode*, dummynode); /* use common `dummynode' */
|
||||
{ // no elements to hash part?
|
||||
t->node = cast_to(LuaNode*, dummynode); // use common `dummynode'
|
||||
lsize = 0;
|
||||
}
|
||||
else
|
||||
|
@ -357,7 +354,7 @@ static void setnodevector(lua_State* L, Table* t, int size)
|
|||
}
|
||||
t->lsizenode = cast_byte(lsize);
|
||||
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);
|
||||
|
@ -382,17 +379,17 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
|
|||
luaG_runerror(L, "table overflow");
|
||||
int oldasize = t->sizearray;
|
||||
int oldhsize = t->lsizenode;
|
||||
LuaNode* nold = t->node; /* save old hash ... */
|
||||
if (nasize > oldasize) /* array part must grow? */
|
||||
LuaNode* nold = t->node; // save old hash ...
|
||||
if (nasize > oldasize) // array part must grow?
|
||||
setarrayvector(L, t, nasize);
|
||||
/* create new hash part with appropriate size */
|
||||
// create new hash part with appropriate size
|
||||
setnodevector(L, t, nhsize);
|
||||
/* used for the migration check at the end */
|
||||
// used for the migration check at the end
|
||||
LuaNode* nnew = t->node;
|
||||
if (nasize < oldasize)
|
||||
{ /* array part must shrink? */
|
||||
{ // array part must shrink?
|
||||
t->sizearray = nasize;
|
||||
/* re-insert elements from vanishing slice */
|
||||
// re-insert elements from vanishing slice
|
||||
for (int i = nasize; i < oldasize; i++)
|
||||
{
|
||||
if (!ttisnil(&t->array[i]))
|
||||
|
@ -402,12 +399,12 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
|
|||
setobjt2t(L, newkey(L, t, &ok), &t->array[i]);
|
||||
}
|
||||
}
|
||||
/* shrink array */
|
||||
// shrink array
|
||||
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;
|
||||
/* re-insert elements from hash part */
|
||||
// re-insert elements from hash part
|
||||
for (int i = twoto(oldhsize) - 1; i >= 0; i--)
|
||||
{
|
||||
LuaNode* old = nold + i;
|
||||
|
@ -419,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(anew == t->array);
|
||||
|
||||
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)
|
||||
{
|
||||
bool tbound = t->node != dummynode || size < t->sizearray;
|
||||
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))))
|
||||
size++;
|
||||
return size;
|
||||
|
@ -451,22 +448,22 @@ void luaH_resizehash(lua_State* L, Table* t, int nhsize)
|
|||
|
||||
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++)
|
||||
nums[i] = 0; /* reset counts */
|
||||
int nasize = numusearray(t, nums); /* count keys in array part */
|
||||
int totaluse = nasize; /* all those keys are integer keys */
|
||||
totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */
|
||||
/* count extra key */
|
||||
nums[i] = 0; // reset counts
|
||||
int nasize = numusearray(t, nums); // count keys in array part
|
||||
int totaluse = nasize; // all those keys are integer keys
|
||||
totaluse += numusehash(t, nums, &nasize); // count keys in hash part
|
||||
// count extra key
|
||||
if (ttisnumber(ek))
|
||||
nasize += countint(nvalue(ek), nums);
|
||||
totaluse++;
|
||||
/* compute new size for array part */
|
||||
// compute new size for array part
|
||||
int na = computesizes(nums, &nasize);
|
||||
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);
|
||||
/* resize the table to new computed sizes */
|
||||
// resize the table to new computed sizes
|
||||
resize(L, t, nasize, nh);
|
||||
}
|
||||
|
||||
|
@ -514,7 +511,7 @@ static LuaNode* getfreepos(Table* t)
|
|||
if (ttisnil(gkey(n)))
|
||||
return n;
|
||||
}
|
||||
return NULL; /* could not find a free place */
|
||||
return NULL; // could not find a free place
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -526,24 +523,24 @@ static LuaNode* getfreepos(Table* t)
|
|||
*/
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
LuaNode* mp = mainposition(t, key);
|
||||
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)
|
||||
{ /* cannot find a free place? */
|
||||
rehash(L, t, key); /* grow table */
|
||||
{ // cannot find a free place?
|
||||
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);
|
||||
}
|
||||
LUAU_ASSERT(n != dummynode);
|
||||
|
@ -551,24 +548,24 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
|
|||
getnodekey(L, &mk, mp);
|
||||
LuaNode* othern = mainposition(t, &mk);
|
||||
if (othern != mp)
|
||||
{ /* is colliding node out of its main position? */
|
||||
/* yes; move colliding node into free position */
|
||||
{ // is colliding node out of its main position?
|
||||
// yes; move colliding node into free position
|
||||
while (othern + gnext(othern) != mp)
|
||||
othern += gnext(othern); /* find previous */
|
||||
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) */
|
||||
othern += gnext(othern); // find previous
|
||||
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)
|
||||
if (gnext(mp) != 0)
|
||||
{
|
||||
gnext(n) += cast_int(mp - n); /* correct 'next' */
|
||||
gnext(mp) = 0; /* now 'mp' is free */
|
||||
gnext(n) += cast_int(mp - n); // correct 'next'
|
||||
gnext(mp) = 0; // now 'mp' is free
|
||||
}
|
||||
setnilvalue(gval(mp));
|
||||
}
|
||||
else
|
||||
{ /* colliding node is in its own main position */
|
||||
/* new node will go into free position */
|
||||
{ // colliding node is in its own main position
|
||||
// new node will go into free position
|
||||
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
|
||||
LUAU_ASSERT(gnext(n) == 0);
|
||||
gnext(mp) = cast_int(n - mp);
|
||||
|
@ -586,7 +583,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* 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))
|
||||
return &t->array[key - 1];
|
||||
else if (t->node != dummynode)
|
||||
|
@ -594,9 +591,9 @@ const TValue* luaH_getnum(Table* t, int key)
|
|||
double nk = cast_num(key);
|
||||
LuaNode* n = hashnum(t, nk);
|
||||
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))
|
||||
return gval(n); /* that's it */
|
||||
return gval(n); // that's it
|
||||
if (gnext(n) == 0)
|
||||
break;
|
||||
n += gnext(n);
|
||||
|
@ -614,9 +611,9 @@ const TValue* luaH_getstr(Table* t, TString* key)
|
|||
{
|
||||
LuaNode* n = hashstr(t, key);
|
||||
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)
|
||||
return gval(n); /* that's it */
|
||||
return gval(n); // that's it
|
||||
if (gnext(n) == 0)
|
||||
break;
|
||||
n += gnext(n);
|
||||
|
@ -640,17 +637,17 @@ const TValue* luaH_get(Table* t, const TValue* key)
|
|||
int k;
|
||||
double n = nvalue(key);
|
||||
luai_num2int(k, n);
|
||||
if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
|
||||
return luaH_getnum(t, k); /* use specialized version */
|
||||
/* else go through */
|
||||
if (luai_numeq(cast_num(k), nvalue(key))) // index is int?
|
||||
return luaH_getnum(t, k); // use specialized version
|
||||
// else go through
|
||||
}
|
||||
default:
|
||||
{
|
||||
LuaNode* n = mainposition(t, key);
|
||||
for (;;)
|
||||
{ /* check whether `key' is somewhere in the chain */
|
||||
{ // check whether `key' is somewhere in the chain
|
||||
if (luaO_rawequalKey(gkey(n), key))
|
||||
return gval(n); /* that's it */
|
||||
return gval(n); // that's it
|
||||
if (gnext(n) == 0)
|
||||
break;
|
||||
n += gnext(n);
|
||||
|
@ -667,23 +664,26 @@ TValue* luaH_set(lua_State* L, Table* t, const TValue* key)
|
|||
if (p != luaO_nilobject)
|
||||
return cast_to(TValue*, p);
|
||||
else
|
||||
{
|
||||
if (ttisnil(key))
|
||||
luaG_runerror(L, "table index is nil");
|
||||
else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
|
||||
luaG_runerror(L, "table index is NaN");
|
||||
else if (ttisvector(key) && luai_vecisnan(vvalue(key)))
|
||||
luaG_runerror(L, "table index contains NaN");
|
||||
return newkey(L, t, key);
|
||||
}
|
||||
return luaH_newkey(L, t, key);
|
||||
}
|
||||
|
||||
TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key)
|
||||
{
|
||||
if (ttisnil(key))
|
||||
luaG_runerror(L, "table index is nil");
|
||||
else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
|
||||
luaG_runerror(L, "table index is NaN");
|
||||
else if (ttisvector(key) && luai_vecisnan(vvalue(key)))
|
||||
luaG_runerror(L, "table index contains NaN");
|
||||
return newkey(L, t, 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))
|
||||
return &t->array[key - 1];
|
||||
/* hash fallback */
|
||||
// hash fallback
|
||||
const TValue* p = luaH_getnum(t, key);
|
||||
if (p != luaO_nilobject)
|
||||
return cast_to(TValue*, p);
|
||||
|
@ -739,9 +739,9 @@ int luaH_getn(Table* t)
|
|||
if (boundary > 0)
|
||||
{
|
||||
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]))
|
||||
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);
|
||||
if (foundboundary > 0)
|
||||
|
@ -767,7 +767,7 @@ int luaH_getn(Table* t)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* validate boundary invariant */
|
||||
// validate boundary invariant
|
||||
LUAU_ASSERT(t->node == dummynode || ttisnil(luaH_getnum(t, j + 1)));
|
||||
return j;
|
||||
}
|
||||
|
@ -812,7 +812,7 @@ Table* luaH_clone(lua_State* L, Table* tt)
|
|||
|
||||
void luaH_clear(Table* tt)
|
||||
{
|
||||
/* clear array part */
|
||||
// clear array part
|
||||
for (int i = 0; i < tt->sizearray; ++i)
|
||||
{
|
||||
setnilvalue(&tt->array[i]);
|
||||
|
@ -820,7 +820,7 @@ void luaH_clear(Table* tt)
|
|||
|
||||
maybesetaboundary(tt, 0);
|
||||
|
||||
/* clear hash part */
|
||||
// clear hash part
|
||||
if (tt->node != dummynode)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -11,12 +11,16 @@
|
|||
|
||||
#define gval2slot(t, v) int(cast_to(LuaNode*, static_cast<const TValue*>(v)) - t->node)
|
||||
|
||||
// reset cache of absent metamethods, cache is updated in luaT_gettm
|
||||
#define invalidateTMcache(t) t->tmcache = 0
|
||||
|
||||
LUAI_FUNC const TValue* luaH_getnum(Table* t, int key);
|
||||
LUAI_FUNC TValue* luaH_setnum(lua_State* L, Table* t, int key);
|
||||
LUAI_FUNC const TValue* luaH_getstr(Table* t, TString* key);
|
||||
LUAI_FUNC TValue* luaH_setstr(lua_State* L, Table* t, TString* key);
|
||||
LUAI_FUNC const TValue* luaH_get(Table* t, const TValue* key);
|
||||
LUAI_FUNC TValue* luaH_set(lua_State* L, Table* t, const TValue* key);
|
||||
LUAI_FUNC TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key);
|
||||
LUAI_FUNC Table* luaH_new(lua_State* L, int narray, int lnhash);
|
||||
LUAI_FUNC void luaH_resizearray(lua_State* L, Table* t, int nasize);
|
||||
LUAI_FUNC void luaH_resizehash(lua_State* L, Table* t, int nhsize);
|
||||
|
@ -26,4 +30,6 @@ LUAI_FUNC int luaH_getn(Table* t);
|
|||
LUAI_FUNC Table* luaH_clone(lua_State* L, Table* tt);
|
||||
LUAI_FUNC void luaH_clear(Table* tt);
|
||||
|
||||
#define luaH_setslot(L, t, slot, key) (invalidateTMcache(t), (slot == luaO_nilobject ? luaH_newkey(L, t, key) : cast_to(TValue*, slot)))
|
||||
|
||||
extern const LuaNode luaH_dummynode;
|
||||
|
|
|
@ -18,13 +18,13 @@ static int foreachi(lua_State* L)
|
|||
int n = lua_objlen(L, 1);
|
||||
for (i = 1; i <= n; i++)
|
||||
{
|
||||
lua_pushvalue(L, 2); /* function */
|
||||
lua_pushinteger(L, i); /* 1st argument */
|
||||
lua_rawgeti(L, 1, i); /* 2nd argument */
|
||||
lua_pushvalue(L, 2); // function
|
||||
lua_pushinteger(L, i); // 1st argument
|
||||
lua_rawgeti(L, 1, i); // 2nd argument
|
||||
lua_call(L, 2, 1);
|
||||
if (!lua_isnil(L, -1))
|
||||
return 1;
|
||||
lua_pop(L, 1); /* remove nil result */
|
||||
lua_pop(L, 1); // remove nil result
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -33,16 +33,16 @@ static int foreach (lua_State* L)
|
|||
{
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
lua_pushnil(L); /* first key */
|
||||
lua_pushnil(L); // first key
|
||||
while (lua_next(L, 1))
|
||||
{
|
||||
lua_pushvalue(L, 2); /* function */
|
||||
lua_pushvalue(L, -3); /* key */
|
||||
lua_pushvalue(L, -3); /* value */
|
||||
lua_pushvalue(L, 2); // function
|
||||
lua_pushvalue(L, -3); // key
|
||||
lua_pushvalue(L, -3); // value
|
||||
lua_call(L, 2, 1);
|
||||
if (!lua_isnil(L, -1))
|
||||
return 1;
|
||||
lua_pop(L, 2); /* remove value and result */
|
||||
lua_pop(L, 2); // remove value and result
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -51,10 +51,10 @@ static int maxn(lua_State* L)
|
|||
{
|
||||
double max = 0;
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_pushnil(L); /* first key */
|
||||
lua_pushnil(L); // first key
|
||||
while (lua_next(L, 1))
|
||||
{
|
||||
lua_pop(L, 1); /* remove value */
|
||||
lua_pop(L, 1); // remove value
|
||||
if (lua_type(L, -1) == LUA_TNUMBER)
|
||||
{
|
||||
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)
|
||||
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) &&
|
||||
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);
|
||||
int n = lua_objlen(L, 1);
|
||||
int pos; /* where to insert new element */
|
||||
int pos; // where to insert new element
|
||||
switch (lua_gettop(L))
|
||||
{
|
||||
case 2:
|
||||
{ /* called with only 2 arguments */
|
||||
pos = n + 1; /* insert new element at the end */
|
||||
{ // called with only 2 arguments
|
||||
pos = n + 1; // insert new element at the end
|
||||
break;
|
||||
}
|
||||
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)
|
||||
moveelements(L, 1, 1, pos, n, pos + 1);
|
||||
break;
|
||||
|
@ -159,7 +159,7 @@ static int tinsert(lua_State* L)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -169,14 +169,14 @@ static int tremove(lua_State* L)
|
|||
int n = lua_objlen(L, 1);
|
||||
int pos = luaL_optinteger(L, 2, n);
|
||||
|
||||
if (!(1 <= pos && pos <= n)) /* position is outside bounds? */
|
||||
return 0; /* nothing to remove */
|
||||
lua_rawgeti(L, 1, pos); /* result = t[pos] */
|
||||
if (!(1 <= pos && pos <= n)) // position is outside bounds?
|
||||
return 0; // nothing to remove
|
||||
lua_rawgeti(L, 1, pos); // result = t[pos]
|
||||
|
||||
moveelements(L, 1, 1, pos + 1, n, pos);
|
||||
|
||||
lua_pushnil(L);
|
||||
lua_rawseti(L, 1, n); /* t[n] = nil */
|
||||
lua_rawseti(L, 1, n); // t[n] = nil
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -192,28 +192,28 @@ static int tmove(lua_State* L)
|
|||
int f = luaL_checkinteger(L, 2);
|
||||
int e = luaL_checkinteger(L, 3);
|
||||
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);
|
||||
|
||||
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");
|
||||
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");
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
moveelements(L, 1, tt, f, e, t);
|
||||
}
|
||||
lua_pushvalue(L, tt); /* return destination table */
|
||||
lua_pushvalue(L, tt); // return destination table
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ static int tconcat(lua_State* L)
|
|||
addfield(L, &b, i);
|
||||
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);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
|
@ -248,8 +248,8 @@ static int tconcat(lua_State* L)
|
|||
|
||||
static int tpack(lua_State* L)
|
||||
{
|
||||
int n = lua_gettop(L); /* number of elements to pack */
|
||||
lua_createtable(L, n, 1); /* create result table */
|
||||
int n = lua_gettop(L); // number of elements to pack
|
||||
lua_createtable(L, n, 1); // create result table
|
||||
|
||||
Table* t = hvalue(L->top - 1);
|
||||
|
||||
|
@ -259,11 +259,11 @@ static int tpack(lua_State* L)
|
|||
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"));
|
||||
setnvalue(nv, n);
|
||||
|
||||
return 1; /* return table */
|
||||
return 1; // return table
|
||||
}
|
||||
|
||||
static int tunpack(lua_State* L)
|
||||
|
@ -274,8 +274,8 @@ static int tunpack(lua_State* L)
|
|||
int i = luaL_optinteger(L, 2, 1);
|
||||
int e = luaL_opt(L, luaL_checkinteger, 3, lua_objlen(L, 1));
|
||||
if (i > e)
|
||||
return 0; /* empty range */
|
||||
unsigned n = (unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
|
||||
return 0; // empty range
|
||||
unsigned n = (unsigned)e - i; // number of elements minus 1 (avoid overflows)
|
||||
if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n)))
|
||||
luaL_error(L, "too many results to unpack");
|
||||
|
||||
|
@ -288,10 +288,10 @@ static int tunpack(lua_State* L)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* push arg[i..e - 1] (to avoid overflows) */
|
||||
// push arg[i..e - 1] (to avoid overflows)
|
||||
for (; i < e; 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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
if (!lua_isnil(L, 2))
|
||||
{ /* function? */
|
||||
{ // function?
|
||||
int res;
|
||||
lua_pushvalue(L, 2);
|
||||
lua_pushvalue(L, a - 1); /* -1 to compensate function */
|
||||
lua_pushvalue(L, b - 2); /* -2 to compensate function and `a' */
|
||||
lua_pushvalue(L, a - 1); // -1 to compensate function
|
||||
lua_pushvalue(L, b - 2); // -2 to compensate function and `a'
|
||||
lua_call(L, 2, 1);
|
||||
res = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return res;
|
||||
}
|
||||
else /* a < b? */
|
||||
else // a < b?
|
||||
return lua_lessthan(L, a, b);
|
||||
}
|
||||
|
||||
static void auxsort(lua_State* L, int l, int u)
|
||||
{
|
||||
while (l < u)
|
||||
{ /* for tail recursion */
|
||||
{ // for tail recursion
|
||||
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, u);
|
||||
if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
|
||||
set2(L, l, u); /* swap a[l] - a[u] */
|
||||
if (sort_comp(L, -1, -2)) // a[u] < a[l]?
|
||||
set2(L, l, u); // swap a[l] - a[u]
|
||||
else
|
||||
lua_pop(L, 2);
|
||||
if (u - l == 1)
|
||||
break; /* only 2 elements */
|
||||
break; // only 2 elements
|
||||
i = (l + u) / 2;
|
||||
lua_rawgeti(L, 1, i);
|
||||
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);
|
||||
else
|
||||
{
|
||||
lua_pop(L, 1); /* remove a[l] */
|
||||
lua_pop(L, 1); // remove a[l]
|
||||
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);
|
||||
else
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
if (u - l == 2)
|
||||
break; /* only 3 elements */
|
||||
lua_rawgeti(L, 1, i); /* Pivot */
|
||||
break; // only 3 elements
|
||||
lua_rawgeti(L, 1, i); // Pivot
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawgeti(L, 1, 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;
|
||||
j = u - 1;
|
||||
for (;;)
|
||||
{ /* invariant: a[l..i] <= P <= a[j..u] */
|
||||
/* repeat ++i until a[i] >= P */
|
||||
{ // invariant: a[l..i] <= P <= a[j..u]
|
||||
// repeat ++i until a[i] >= P
|
||||
while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2))
|
||||
{
|
||||
if (i >= u)
|
||||
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))
|
||||
{
|
||||
if (j <= l)
|
||||
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)
|
||||
{
|
||||
lua_pop(L, 3); /* pop pivot, a[i], a[j] */
|
||||
lua_pop(L, 3); // pop pivot, a[i], a[j]
|
||||
break;
|
||||
}
|
||||
set2(L, i, j);
|
||||
}
|
||||
lua_rawgeti(L, 1, u - 1);
|
||||
lua_rawgeti(L, 1, 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] */
|
||||
/* adjust so that smaller half is in [j..i] and larger one in [l..u] */
|
||||
set2(L, u - 1, i); // swap pivot (a[u-1]) with a[i]
|
||||
// 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]
|
||||
if (i - l < u - i)
|
||||
{
|
||||
j = l;
|
||||
|
@ -403,23 +403,23 @@ static void auxsort(lua_State* L, int l, int u)
|
|||
i = u;
|
||||
u = j - 2;
|
||||
}
|
||||
auxsort(L, j, i); /* call recursively the smaller one */
|
||||
} /* repeat the routine for the larger one */
|
||||
auxsort(L, j, i); // call recursively the smaller one
|
||||
} // repeat the routine for the larger one
|
||||
}
|
||||
|
||||
static int sort(lua_State* L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
int n = lua_objlen(L, 1);
|
||||
luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */
|
||||
if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */
|
||||
luaL_checkstack(L, 40, ""); // assume array is smaller than 2^40
|
||||
if (!lua_isnoneornil(L, 2)) // is there a 2nd argument?
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
// }======================================================
|
||||
|
||||
static int tcreate(lua_State* L)
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
// clang-format off
|
||||
const char* const luaT_typenames[] = {
|
||||
/* ORDER TYPE */
|
||||
// ORDER TYPE
|
||||
"nil",
|
||||
"boolean",
|
||||
|
||||
|
@ -31,7 +31,7 @@ const char* const luaT_typenames[] = {
|
|||
};
|
||||
|
||||
const char* const luaT_eventname[] = {
|
||||
/* ORDER TM */
|
||||
// ORDER TM
|
||||
|
||||
"__index",
|
||||
"__newindex",
|
||||
|
@ -70,12 +70,12 @@ void luaT_init(lua_State* L)
|
|||
for (i = 0; i < LUA_T_COUNT; 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++)
|
||||
{
|
||||
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);
|
||||
LUAU_ASSERT(event <= TM_EQ);
|
||||
if (ttisnil(tm))
|
||||
{ /* no tag method? */
|
||||
events->tmcache |= cast_byte(1u << event); /* cache this fact */
|
||||
{ // no tag method?
|
||||
events->tmcache |= cast_byte(1u << event); // cache this fact
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -20,7 +20,7 @@ typedef enum
|
|||
TM_ITER,
|
||||
TM_LEN,
|
||||
|
||||
TM_EQ, /* last tag method with `fast' access */
|
||||
TM_EQ, // last tag method with `fast' access
|
||||
|
||||
|
||||
TM_ADD,
|
||||
|
@ -37,7 +37,7 @@ typedef enum
|
|||
TM_CONCAT,
|
||||
TM_TYPE,
|
||||
|
||||
TM_N /* number of elements in the enum */
|
||||
TM_N // number of elements in the enum
|
||||
} TMS;
|
||||
// clang-format on
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
#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
|
||||
|
||||
/* 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 sizeudata(len) (offsetof(Udata, data) + len)
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
#define iscont(p) ((*(p)&0xC0) == 0x80)
|
||||
|
||||
/* from strlib */
|
||||
/* translate a relative string position: negative means back from end */
|
||||
// from strlib
|
||||
// translate a relative string position: negative means back from end
|
||||
static int u_posrelat(int pos, size_t len)
|
||||
{
|
||||
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};
|
||||
const unsigned char* s = (const unsigned char*)o;
|
||||
unsigned int c = s[0];
|
||||
unsigned int res = 0; /* final result */
|
||||
if (c < 0x80) /* ascii? */
|
||||
unsigned int res = 0; // final result
|
||||
if (c < 0x80) // ascii?
|
||||
res = c;
|
||||
else
|
||||
{
|
||||
int count = 0; /* to count number of continuation bytes */
|
||||
int count = 0; // to count number of continuation bytes
|
||||
while (c & 0x40)
|
||||
{ /* still have continuation bytes? */
|
||||
int cc = s[++count]; /* read next byte */
|
||||
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
|
||||
return NULL; /* invalid byte sequence */
|
||||
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
|
||||
c <<= 1; /* to test next bit */
|
||||
{ // still have continuation bytes?
|
||||
int cc = s[++count]; // read next byte
|
||||
if ((cc & 0xC0) != 0x80) // not a continuation byte?
|
||||
return NULL; // invalid byte sequence
|
||||
res = (res << 6) | (cc & 0x3F); // add lower 6 bits from cont. byte
|
||||
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])
|
||||
return NULL; /* invalid byte sequence */
|
||||
s += count; /* skip continuation bytes read */
|
||||
return NULL; // invalid byte sequence
|
||||
s += count; // skip continuation bytes read
|
||||
}
|
||||
if (val)
|
||||
*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);
|
||||
if (s1 == NULL)
|
||||
{ /* conversion error? */
|
||||
lua_pushnil(L); /* return nil ... */
|
||||
lua_pushinteger(L, posi + 1); /* ... and current position */
|
||||
{ // conversion error?
|
||||
lua_pushnil(L); // return nil ...
|
||||
lua_pushinteger(L, posi + 1); // ... and current position
|
||||
return 2;
|
||||
}
|
||||
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, pose <= (int)len, 3, "out of range");
|
||||
if (posi > pose)
|
||||
return 0; /* empty interval; return no values */
|
||||
if (pose - posi >= INT_MAX) /* (int -> int) overflow? */
|
||||
return 0; // empty interval; return no values
|
||||
if (pose - posi >= INT_MAX) // (int -> int) overflow?
|
||||
luaL_error(L, "string slice too long");
|
||||
n = (int)(pose - posi) + 1;
|
||||
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
|
||||
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);
|
||||
if (x < 0x80) /* ascii? */
|
||||
if (x < 0x80) // ascii?
|
||||
buff[UTF8BUFFSZ - 1] = cast_to(char, x);
|
||||
else
|
||||
{ /* need continuation bytes */
|
||||
unsigned int mfb = 0x3f; /* maximum that fits in first byte */
|
||||
{ // need continuation bytes
|
||||
unsigned int mfb = 0x3f; // maximum that fits in first byte
|
||||
do
|
||||
{ /* add continuation bytes */
|
||||
{ // add continuation bytes
|
||||
buff[UTF8BUFFSZ - (n++)] = cast_to(char, 0x80 | (x & 0x3f));
|
||||
x >>= 6; /* remove added bits */
|
||||
mfb >>= 1; /* now there is one less bit available in first byte */
|
||||
} while (x > mfb); /* still needs continuation byte? */
|
||||
buff[UTF8BUFFSZ - n] = cast_to(char, (~mfb << 1) | x); /* add first byte */
|
||||
x >>= 6; // remove added bits
|
||||
mfb >>= 1; // now there is one less bit available in first byte
|
||||
} while (x > mfb); // still needs continuation byte?
|
||||
buff[UTF8BUFFSZ - n] = cast_to(char, (~mfb << 1) | x); // add first byte
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
@ -162,9 +162,9 @@ static int utfchar(lua_State* L)
|
|||
char buff[UTF8BUFFSZ];
|
||||
const char* charstr;
|
||||
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int n = lua_gettop(L); // number of arguments
|
||||
if (n == 1)
|
||||
{ /* optimize common case of single char */
|
||||
{ // optimize common case of single char
|
||||
int l = buffutfchar(L, 1, buff, &charstr);
|
||||
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");
|
||||
if (n == 0)
|
||||
{
|
||||
/* find beginning of current byte sequence */
|
||||
// find beginning of current byte sequence
|
||||
while (posi > 0 && iscont(s + posi))
|
||||
posi--;
|
||||
}
|
||||
|
@ -207,9 +207,9 @@ static int byteoffset(lua_State* L)
|
|||
if (n < 0)
|
||||
{
|
||||
while (n < 0 && posi > 0)
|
||||
{ /* move back */
|
||||
{ // move back
|
||||
do
|
||||
{ /* find beginning of previous character */
|
||||
{ // find beginning of previous character
|
||||
posi--;
|
||||
} while (posi > 0 && iscont(s + posi));
|
||||
n++;
|
||||
|
@ -217,20 +217,20 @@ static int byteoffset(lua_State* L)
|
|||
}
|
||||
else
|
||||
{
|
||||
n--; /* do not move for 1st character */
|
||||
n--; // do not move for 1st character
|
||||
while (n > 0 && posi < (int)len)
|
||||
{
|
||||
do
|
||||
{ /* find beginning of next character */
|
||||
{ // find beginning of next character
|
||||
posi++;
|
||||
} while (iscont(s + posi)); /* (cannot pass final '\0') */
|
||||
} while (iscont(s + posi)); // (cannot pass final '\0')
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n == 0) /* did it find given character? */
|
||||
if (n == 0) // did it find given character?
|
||||
lua_pushinteger(L, posi + 1);
|
||||
else /* no such character */
|
||||
else // no such character
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
@ -240,16 +240,16 @@ static int iter_aux(lua_State* L)
|
|||
size_t len;
|
||||
const char* s = luaL_checklstring(L, 1, &len);
|
||||
int n = lua_tointeger(L, 2) - 1;
|
||||
if (n < 0) /* first iteration? */
|
||||
n = 0; /* start from here */
|
||||
if (n < 0) // first iteration?
|
||||
n = 0; // start from here
|
||||
else if (n < (int)len)
|
||||
{
|
||||
n++; /* skip current byte */
|
||||
n++; // skip current byte
|
||||
while (iscont(s + n))
|
||||
n++; /* and its continuations */
|
||||
n++; // and its continuations
|
||||
}
|
||||
if (n >= (int)len)
|
||||
return 0; /* no more codepoints */
|
||||
return 0; // no more codepoints
|
||||
else
|
||||
{
|
||||
int code;
|
||||
|
@ -271,7 +271,7 @@ static int iter_codes(lua_State* L)
|
|||
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]*"
|
||||
|
||||
static const luaL_Reg funcs[] = {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauLenTM, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNicerMethodErrors, false)
|
||||
|
||||
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
||||
#ifdef __clang__
|
||||
|
@ -33,7 +33,7 @@ LUAU_FASTFLAGVARIABLE(LuauLenTM, false)
|
|||
// 3. VM_PROTECT macro saves savedpc and restores base for you; most external calls need to be wrapped into that. However, it does NOT restore
|
||||
// ra/rb/rc!
|
||||
// 4. When copying an object to any existing object as a field, generally speaking you need to call luaC_barrier! Be careful with all setobj calls
|
||||
// 5. To make 4 easier to follow, please use setobj2s for copies to stack and setobj for other copies.
|
||||
// 5. To make 4 easier to follow, please use setobj2s for copies to stack, setobj2t for writes to tables, and setobj for other copies.
|
||||
// 6. You can define HARDSTACKTESTS in llimits.h which will aggressively realloc stack; with address sanitizer this should be effective at finding
|
||||
// stack corruption bugs
|
||||
// 7. Many external Lua functions can call GC! GC will *not* traverse pointers to new objects that aren't reachable from Lua root. Be careful when
|
||||
|
@ -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_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_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__)
|
||||
#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, 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);
|
||||
|
||||
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);
|
||||
if (!ttisfunction(tm))
|
||||
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);
|
||||
L->top++; /* stack space pre-allocated by the caller */
|
||||
setobj2s(L, func, tm); /* tag method is the new function to be called */
|
||||
L->top++; // stack space pre-allocated by the caller
|
||||
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)
|
||||
|
@ -256,7 +257,7 @@ LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata)
|
|||
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;
|
||||
LUAU_ASSERT(L->ci->top <= L->stack_last);
|
||||
|
||||
|
@ -458,7 +459,7 @@ static void luau_execute(lua_State* L)
|
|||
|
||||
if (LUAU_LIKELY(ttisstring(gkey(n)) && tsvalue(gkey(n)) == tsvalue(kv) && !ttisnil(gval(n)) && !h->readonly))
|
||||
{
|
||||
setobj(L, gval(n), ra);
|
||||
setobj2t(L, gval(n), ra);
|
||||
luaC_barriert(L, h, ra);
|
||||
VM_NEXT();
|
||||
}
|
||||
|
@ -672,7 +673,7 @@ static void luau_execute(lua_State* L)
|
|||
// fast-path: value is in expected slot
|
||||
if (LUAU_LIKELY(ttisstring(gkey(n)) && tsvalue(gkey(n)) == tsvalue(kv) && !ttisnil(gval(n)) && !h->readonly))
|
||||
{
|
||||
setobj(L, gval(n), ra);
|
||||
setobj2t(L, gval(n), ra);
|
||||
luaC_barriert(L, h, ra);
|
||||
VM_NEXT();
|
||||
}
|
||||
|
@ -684,7 +685,7 @@ static void luau_execute(lua_State* L)
|
|||
int cachedslot = gval2slot(h, res);
|
||||
// save cachedslot to accelerate future lookups; patches currently executing instruction since pc-2 rolls back two pc++
|
||||
VM_PATCH_C(pc - 2, cachedslot);
|
||||
setobj(L, res, ra);
|
||||
setobj2t(L, res, ra);
|
||||
luaC_barriert(L, h, ra);
|
||||
VM_NEXT();
|
||||
}
|
||||
|
@ -929,6 +930,10 @@ static void luau_execute(lua_State* L)
|
|||
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++
|
||||
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
|
||||
|
@ -966,6 +971,10 @@ static void luau_execute(lua_State* L)
|
|||
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++
|
||||
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
|
||||
|
@ -973,6 +982,10 @@ static void luau_execute(lua_State* L)
|
|||
// slow-path: handles non-table __index
|
||||
setobj2s(L, ra + 1, rb);
|
||||
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 argend = L->base + p->numparams;
|
||||
while (argi < argend)
|
||||
setnilvalue(argi++); /* complete missing arguments */
|
||||
setnilvalue(argi++); // complete missing arguments
|
||||
L->top = p->is_vararg ? argi : ci->top;
|
||||
|
||||
// reentry
|
||||
|
@ -2074,7 +2087,7 @@ static void luau_execute(lua_State* L)
|
|||
{
|
||||
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)));
|
||||
VM_NEXT();
|
||||
|
@ -2214,7 +2227,7 @@ static void luau_execute(lua_State* L)
|
|||
|
||||
if (ttisfunction(ra))
|
||||
{
|
||||
/* will be called during FORGLOOP */
|
||||
// will be called during FORGLOOP
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2225,16 +2238,16 @@ static void luau_execute(lua_State* L)
|
|||
setobj2s(L, ra + 1, ra);
|
||||
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);
|
||||
|
||||
VM_PROTECT(luaD_call(L, ra, 3));
|
||||
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));
|
||||
|
||||
/* 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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
/* 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 */
|
||||
// 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
|
||||
}
|
||||
else if (ttistable(ra))
|
||||
{
|
||||
/* set up registers for builtin iteration */
|
||||
// set up registers for builtin iteration
|
||||
setobj2s(L, ra + 1, ra);
|
||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
|
||||
setnilvalue(ra);
|
||||
|
@ -2344,7 +2357,7 @@ static void luau_execute(lua_State* L)
|
|||
setobjs2s(L, ra + 3 + 1, ra + 1);
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
setnilvalue(ra);
|
||||
/* ra+1 is already the table */
|
||||
// ra+1 is already the table
|
||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
|
||||
}
|
||||
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))
|
||||
{
|
||||
setnilvalue(ra);
|
||||
/* ra+1 is already the table */
|
||||
// ra+1 is already the table
|
||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
|
||||
}
|
||||
else if (!ttisfunction(ra))
|
||||
|
@ -2619,8 +2632,8 @@ static void luau_execute(lua_State* L)
|
|||
LUAU_ASSERT(cast_int(L->top - base) >= numparams);
|
||||
|
||||
// move fixed parameters to final position
|
||||
StkId fixed = base; /* first fixed argument */
|
||||
base = L->top; /* final position of first argument */
|
||||
StkId fixed = base; // first fixed argument
|
||||
base = L->top; // final position of first argument
|
||||
|
||||
for (int i = 0; i < numparams; ++i)
|
||||
{
|
||||
|
@ -2983,6 +2996,56 @@ static void luau_execute(lua_State* L)
|
|||
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
|
||||
default:
|
||||
LUAU_ASSERT(!"Unknown opcode");
|
||||
|
@ -3032,7 +3095,7 @@ int luau_precall(lua_State* L, StkId func, int nresults)
|
|||
StkId argi = L->top;
|
||||
StkId argend = L->base + ccl->l.p->numparams;
|
||||
while (argi < argend)
|
||||
setnilvalue(argi++); /* complete missing arguments */
|
||||
setnilvalue(argi++); // complete missing arguments
|
||||
L->top = ccl->l.p->is_vararg ? argi : ci->top;
|
||||
|
||||
L->ci->savedpc = ccl->l.p->code;
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauLenTM)
|
||||
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
|
||||
|
||||
const TValue* luaV_tonumber(const TValue* obj, TValue* n)
|
||||
|
@ -65,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
|
||||
// values will be preserved even if they go past stack_last
|
||||
LUAU_ASSERT((L->top + 3) < (L->stack + L->stacksize));
|
||||
setobj2s(L, L->top, f); /* push function */
|
||||
setobj2s(L, L->top + 1, p1); /* 1st argument */
|
||||
setobj2s(L, L->top + 2, p2); /* 2nd argument */
|
||||
setobj2s(L, L->top, f); // push function
|
||||
setobj2s(L, L->top + 1, p1); // 1st argument
|
||||
setobj2s(L, L->top + 2, p2); // 2nd argument
|
||||
luaD_checkstack(L, 3);
|
||||
L->top += 3;
|
||||
luaD_call(L, L->top - 3, 1);
|
||||
|
@ -87,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
|
||||
// values will be preserved even if they go past stack_last
|
||||
LUAU_ASSERT((L->top + 4) < (L->stack + L->stacksize));
|
||||
setobj2s(L, L->top, f); /* push function */
|
||||
setobj2s(L, L->top + 1, p1); /* 1st argument */
|
||||
setobj2s(L, L->top + 2, p2); /* 2nd argument */
|
||||
setobj2s(L, L->top + 3, p3); /* 3th argument */
|
||||
setobj2s(L, L->top, f); // push function
|
||||
setobj2s(L, L->top + 1, p1); // 1st argument
|
||||
setobj2s(L, L->top + 2, p2); // 2nd argument
|
||||
setobj2s(L, L->top + 3, p3); // 3th argument
|
||||
luaD_checkstack(L, 4);
|
||||
L->top += 4;
|
||||
luaD_call(L, L->top - 4, 0);
|
||||
|
@ -103,21 +103,21 @@ void luaV_gettable(lua_State* L, const TValue* t, TValue* key, StkId val)
|
|||
{
|
||||
const TValue* tm;
|
||||
if (ttistable(t))
|
||||
{ /* `t' is a table? */
|
||||
{ // `t' is a table?
|
||||
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)
|
||||
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)
|
||||
{ /* or no TM? */
|
||||
{ // or no TM?
|
||||
setobj2s(L, val, res);
|
||||
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)))
|
||||
luaG_indexerror(L, t, key);
|
||||
|
@ -126,7 +126,7 @@ void luaV_gettable(lua_State* L, const TValue* t, TValue* key, StkId val)
|
|||
callTMres(L, val, tm, t, key);
|
||||
return;
|
||||
}
|
||||
t = tm; /* else repeat with `tm' */
|
||||
t = tm; // else repeat with `tm'
|
||||
}
|
||||
luaG_runerror(L, "'__index' chain too long; possible loop");
|
||||
}
|
||||
|
@ -139,34 +139,60 @@ void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
|
|||
{
|
||||
const TValue* tm;
|
||||
if (ttistable(t))
|
||||
{ /* `t' is a table? */
|
||||
{ // `t' is a table?
|
||||
Table* h = hvalue(t);
|
||||
|
||||
if (h->readonly)
|
||||
luaG_readonlyerror(L);
|
||||
if (FFlag::LuauBetterNewindex)
|
||||
{
|
||||
const TValue* oldval = luaH_get(h, key);
|
||||
|
||||
TValue* oldval = luaH_set(L, h, key); /* do a primitive 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 (h->readonly)
|
||||
luaG_readonlyerror(L);
|
||||
|
||||
L->cachedslot = gval2slot(h, oldval); /* remember slot to accelerate future lookups */
|
||||
// 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);
|
||||
|
||||
if (!ttisnil(oldval) || /* result is no nil? */
|
||||
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
|
||||
{ /* or no TM? */
|
||||
setobj2t(L, oldval, val);
|
||||
luaC_barriert(L, h, val);
|
||||
return;
|
||||
L->cachedslot = gval2slot(h, newval); // remember slot to accelerate future lookups
|
||||
|
||||
setobj2t(L, newval, val);
|
||||
luaC_barriert(L, h, val);
|
||||
return;
|
||||
}
|
||||
|
||||
// fallthrough to metamethod
|
||||
}
|
||||
else
|
||||
{
|
||||
if (h->readonly)
|
||||
luaG_readonlyerror(L);
|
||||
|
||||
TValue* oldval = luaH_set(L, h, key); // do a primitive set
|
||||
|
||||
L->cachedslot = gval2slot(h, oldval); // remember slot to accelerate future lookups
|
||||
|
||||
if (!ttisnil(oldval) || // result is no nil?
|
||||
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
|
||||
{ // or no TM?
|
||||
setobj2t(L, oldval, val);
|
||||
luaC_barriert(L, h, val);
|
||||
return;
|
||||
}
|
||||
// else will try the tag method
|
||||
}
|
||||
/* else will try the tag method */
|
||||
}
|
||||
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
|
||||
luaG_indexerror(L, t, key);
|
||||
|
||||
if (ttisfunction(tm))
|
||||
{
|
||||
callTM(L, tm, t, key, val);
|
||||
return;
|
||||
}
|
||||
/* else repeat with `tm' */
|
||||
setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */
|
||||
// else repeat with `tm'
|
||||
setobj(L, &temp, tm); // avoid pointing inside table (may rehash)
|
||||
t = &temp;
|
||||
}
|
||||
luaG_runerror(L, "'__newindex' chain too long; possible loop");
|
||||
|
@ -174,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)
|
||||
{
|
||||
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))
|
||||
tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
|
||||
tm = luaT_gettmbyobj(L, p2, event); // try second operand
|
||||
if (ttisnil(tm))
|
||||
return 0;
|
||||
callTMres(L, res, tm, p1, p2);
|
||||
|
@ -188,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* tm2;
|
||||
if (tm1 == NULL)
|
||||
return NULL; /* no metamethod */
|
||||
return NULL; // no metamethod
|
||||
if (mt1 == mt2)
|
||||
return tm1; /* same metatables => same metamethods */
|
||||
return tm1; // same metatables => same metamethods
|
||||
tm2 = fasttm(L, mt2, event);
|
||||
if (tm2 == NULL)
|
||||
return NULL; /* no metamethod */
|
||||
if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */
|
||||
return NULL; // no metamethod
|
||||
if (luaO_rawequalObj(tm1, tm2)) // same metamethods?
|
||||
return tm1;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -204,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* tm2;
|
||||
if (ttisnil(tm1))
|
||||
return -1; /* no metamethod? */
|
||||
return -1; // no metamethod?
|
||||
tm2 = luaT_gettmbyobj(L, p2, event);
|
||||
if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */
|
||||
if (!luaO_rawequalObj(tm1, tm2)) // different metamethods?
|
||||
return -1;
|
||||
callTMres(L, L->top, tm1, p1, p2);
|
||||
return !l_isfalse(L->top);
|
||||
|
@ -253,9 +279,9 @@ int luaV_lessequal(lua_State* L, const TValue* l, const TValue* r)
|
|||
return luai_numle(nvalue(l), nvalue(r));
|
||||
else if (ttisstring(l))
|
||||
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;
|
||||
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);
|
||||
return !res;
|
||||
}
|
||||
|
@ -273,7 +299,7 @@ int luaV_equalval(lua_State* L, const TValue* t1, const TValue* t2)
|
|||
case LUA_TVECTOR:
|
||||
return luai_veceq(vvalue(t1), vvalue(t2));
|
||||
case LUA_TBOOLEAN:
|
||||
return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
|
||||
return bvalue(t1) == bvalue(t2); // true must be 1 !!
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
return pvalue(t1) == pvalue(t2);
|
||||
case LUA_TUSERDATA:
|
||||
|
@ -281,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);
|
||||
if (!tm)
|
||||
return uvalue(t1) == uvalue(t2);
|
||||
break; /* will try TM */
|
||||
break; // will try TM
|
||||
}
|
||||
case LUA_TTABLE:
|
||||
{
|
||||
tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
|
||||
if (!tm)
|
||||
return hvalue(t1) == hvalue(t2);
|
||||
break; /* will try TM */
|
||||
break; // will try TM
|
||||
}
|
||||
default:
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -302,21 +328,21 @@ void luaV_concat(lua_State* L, int total, int last)
|
|||
do
|
||||
{
|
||||
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 (!call_binTM(L, top - 2, top - 1, top - 2, TM_CONCAT))
|
||||
luaG_concaterror(L, top - 2, top - 1);
|
||||
}
|
||||
else if (tsvalue(top - 1)->len == 0) /* second op is empty? */
|
||||
(void)tostring(L, top - 2); /* result is first op (as string) */
|
||||
else if (tsvalue(top - 1)->len == 0) // second op is empty?
|
||||
(void)tostring(L, top - 2); // result is first op (as string)
|
||||
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;
|
||||
char* buffer;
|
||||
int i;
|
||||
/* collect total length */
|
||||
// collect total length
|
||||
for (n = 1; n < total && tostring(L, top - n - 1); n++)
|
||||
{
|
||||
size_t l = tsvalue(top - n - 1)->len;
|
||||
|
@ -340,7 +366,7 @@ void luaV_concat(lua_State* L, int total, int last)
|
|||
|
||||
tl = 0;
|
||||
for (i = n; i > 0; i--)
|
||||
{ /* concat all strings */
|
||||
{ // concat all strings
|
||||
size_t l = tsvalue(top - i)->len;
|
||||
memcpy(buffer + tl, svalue(top - i), l);
|
||||
tl += l;
|
||||
|
@ -355,9 +381,9 @@ void luaV_concat(lua_State* L, int total, int last)
|
|||
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;
|
||||
} 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)
|
||||
|
@ -476,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)
|
||||
{
|
||||
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;
|
||||
switch (ttype(rb))
|
||||
{
|
||||
|
@ -527,5 +530,5 @@ void luaV_dolen(lua_State* L, StkId ra, const TValue* rb)
|
|||
|
||||
StkId res = callTMres(L, ra, tm, rb, luaO_nilobject);
|
||||
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
|
||||
}
|
||||
|
|
|
@ -26,15 +26,3 @@ This document tracks unimplemented RFCs.
|
|||
[RFC: Lower bounds calculation](https://github.com/Roblox/luau/blob/master/rfcs/lower-bounds-calculation.md)
|
||||
|
||||
**Status**: Implemented but not fully rolled out yet.
|
||||
|
||||
## never and unknown types
|
||||
|
||||
[RFC: never and unknown types](https://github.com/Roblox/luau/blob/master/rfcs/never-and-unknown-types.md)
|
||||
|
||||
**Status**: Needs implementation
|
||||
|
||||
## __len metamethod for tables and rawlen function
|
||||
|
||||
[RFC: Support __len metamethod for tables and rawlen function](https://github.com/Roblox/luau/blob/master/rfcs/len-metamethod-rawlen.md)
|
||||
|
||||
**Status**: Needs implementation
|
||||
|
|
129
rfcs/disallow-proposals-leading-to-ambiguity-in-grammar.md
Normal file
129
rfcs/disallow-proposals-leading-to-ambiguity-in-grammar.md
Normal file
|
@ -0,0 +1,129 @@
|
|||
# Disallow `name T` and `name(T)` in future syntactic extensions for type annotations
|
||||
|
||||
## Summary
|
||||
|
||||
We propose to disallow the syntax ``<name> `('`` as well as `<name> <type>` in future syntax extensions for type annotations to ensure that all existing programs continue to parse correctly. This still keeps the door open for future syntax extensions of different forms such as ``<name> `<' <type> `>'``.
|
||||
|
||||
## Motivation
|
||||
|
||||
Lua and by extension Luau's syntax is very free form, which means that when the parser finishes parsing a node, it doesn't try to look for a semi-colon or any termination token e.g. a `{` to start a block, or `;` to end a statement, or a newline, etc. It just immediately invokes the next parser to figure out how to parse the next node based on the remainder's starting token.
|
||||
|
||||
That feature is sometimes quite troublesome when we want to add new syntax.
|
||||
|
||||
We have had cases where we talked about using syntax like `setmetatable(T, MT)` and `keyof T`. They all look innocent, but when you look beyond that, and try to apply it onto Luau's grammar, things break down really fast.
|
||||
|
||||
### `F(T)`?
|
||||
|
||||
An example that _will_ cause a change in semantics:
|
||||
|
||||
```
|
||||
local t: F
|
||||
(u):m()
|
||||
```
|
||||
|
||||
where today, `local t: F` is one statement, and `(u):m()` is another. If we had the syntax for `F(T)` here, it becomes invalid input because it gets parsed as
|
||||
|
||||
```
|
||||
local t: F(u)
|
||||
:m()
|
||||
```
|
||||
|
||||
This is important because of the `setmetatable(T, MT)` case:
|
||||
|
||||
```
|
||||
type Foo = setmetatable({ x: number }, { ... })
|
||||
```
|
||||
|
||||
For `setmetatable`, the parser isn't sure whether `{}` is actually a type or an expression, because _today_ `setmetatable` is parsed as a type reference, and `({}, {})` is the remainder that we'll attempt to parse as a statement. This means `{ x: number }` is invalid table _literal_. Recovery by backtracking is technically possible here, but this means performance loss on invalid input + may introduce false positives wrt how things are parsed. We'd much rather take a very strict stance about how things get parsed.
|
||||
|
||||
### `F T`?
|
||||
|
||||
An example that _will_ cause a change in semantics:
|
||||
|
||||
```
|
||||
local function f(t): F T
|
||||
(t or u):m()
|
||||
end
|
||||
```
|
||||
|
||||
where today, the return type annotation `F T` is simply parsed as just `F`, followed by a ambiguous parse error from the statement `T(t or u)` because its `(` is on the next line. If at some point in the future we were to allow `T` followed by `(` on the next line, then there's yet another semantic change. `F T` could be parsed as a type annotation and the first statement is `(t or u):m()` instead of `F` followed by `T(t or u):m()`.
|
||||
|
||||
For `keyof`, here's a practical example of the above issue:
|
||||
|
||||
```
|
||||
type Vec2 = {x: number, y: number}
|
||||
|
||||
local function f(t, u): keyof Vec2
|
||||
(t or u):m()
|
||||
end
|
||||
```
|
||||
|
||||
There's three possible outcomes:
|
||||
1. Return type of `f` is `keyof`, statement throws a parse error because `(` is on the next line after `Vec2`,
|
||||
2. Return type of `f` is `keyof Vec2` and next statement is `(t or u):m()`, or
|
||||
3. Return type of `f` is `keyof` and next statement is `Vec2(t or u):m()` (if we allow `(` on the next line to be part of previous line).
|
||||
|
||||
This particular case is even worse when we keep going:
|
||||
|
||||
```
|
||||
local function f(t): F
|
||||
T(t or u):m()
|
||||
end
|
||||
```
|
||||
|
||||
```
|
||||
local function f(t): F T
|
||||
{1, 2, 3}
|
||||
end
|
||||
```
|
||||
|
||||
where today, `F` is the return type annotation of `f`, and `T(t or u):m()`/`T{1, 2, 3}` is the first statement, respectively.
|
||||
|
||||
Adding some syntax for `F T` **will** cause the parser to change the semantics of the above three examples.
|
||||
|
||||
### But what about `typeof(...)`?
|
||||
|
||||
This syntax is grandfathered in because the parser supported `typeof(...)` before we stabilized our syntax, and especially before type annotations were released to the public, so we didn't need to worry about compatibility here. We are very glad that we used parentheses in this case, because it's natural for expressions to belong within parentheses `()`, and types to belong within angles `<>`.
|
||||
|
||||
## The One Exception with a caveat
|
||||
|
||||
This is a strict requirement!
|
||||
|
||||
`function() -> ()` has been talked about in the past, and this one is different despite falling under the same category as ``<name> `('``. The token `function` is in actual fact a "hard keyword," meaning that it cannot be parsed as a type annotation because it is not an identifier, just a keyword.
|
||||
|
||||
Likewise, we also have talked about adding standalone `function` as a type annotation (semantics of it is irrelevant for this RFC)
|
||||
|
||||
It's possible that we may end up adding both, but the requirements are as such:
|
||||
1. `function() -> ()` must be added first before standalone `function`, OR
|
||||
2. `function` can be added first, but with a future-proofing parse error if `<` or `(` follows after it
|
||||
|
||||
If #1 is what ends up happening, there's not much to worry about because the type annotation parser will parse greedily already, so any new valid input will remain valid and have same semantics, except it also allows omitting of `(` and `<`.
|
||||
|
||||
If #2 is what ends up happening, there could be a problem if we didn't future-proof against `<` and `(` to follow `function`:
|
||||
|
||||
```
|
||||
return f :: function(T) -> U
|
||||
```
|
||||
|
||||
which would be a parse error because at the point of `(` we expect one of `until`, `end`, or `EOF`, and
|
||||
|
||||
```
|
||||
return f :: function<a>(a) -> a
|
||||
```
|
||||
|
||||
which would also be a parse error by the time we reach `->`, that is the production of the above is semantically equivalent to `(f < a) > (a)` which would compare whether the value of `f` is less than the value of `a`, then whether the result of that value is greater than `a`.
|
||||
|
||||
## Alternatives
|
||||
|
||||
Only allow these syntax when used inside parentheses e.g. `(F T)` or `(F(T))`. This makes it inconsistent with the existing `typeof(...)` type annotation, and changing that over is also breaking change.
|
||||
|
||||
Support backtracking in the parser, so if `: MyType(t or u):m()` is invalid syntax, revert and parse `MyType` as a type, and `(t or u):m()` as an expression statement. Even so, this option is terrible for:
|
||||
1. parsing performance (backtracking means losing progress on invalid input),
|
||||
2. user experience (why was this annotation parsed as `X(...)` instead of `X` followed by a statement `(...)`),
|
||||
3. has false positives (`foo(bar)(baz)` may be parsed as `foo(bar)` as the type annotation and `(baz)` is the remainder to parse)
|
||||
|
||||
## Drawbacks
|
||||
|
||||
To be able to expose some kind of type-level operations using `F<T>` syntax, means one of the following must be chosen:
|
||||
1. introduce the concept of "magic type functions" into type inference, or
|
||||
2. introduce them into the prelude as `export type F<T> = ...` (where `...` is to be read as "we haven't decided")
|
|
@ -1,5 +1,7 @@
|
|||
# Support `__len` metamethod for tables and `rawlen` function
|
||||
|
||||
**Status**: Implemented
|
||||
|
||||
## Summary
|
||||
|
||||
`__len` metamethod will be called by `#` operator on tables, matching Lua 5.2
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# never and unknown types
|
||||
|
||||
**Status**: Implemented
|
||||
|
||||
## Summary
|
||||
|
||||
Add `unknown` and `never` types that are inhabited by everything and nothing respectively.
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue