Sync to upstream/release/539 (#625)
This commit is contained in:
parent
4658219df2
commit
1b20fcd43c
111 changed files with 3548 additions and 1494 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,4 +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>
|
||||
|
@ -71,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
|
||||
|
|
|
@ -42,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;
|
||||
|
||||
|
@ -107,6 +109,9 @@ struct ConstraintGraphBuilder
|
|||
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(const ScopePtr& scope, const AstArray<AstExpr*>& exprs);
|
||||
|
||||
|
@ -153,9 +158,10 @@ struct ConstraintGraphBuilder
|
|||
* 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(const ScopePtr& scope, AstType* ty);
|
||||
TypeId resolveType(const ScopePtr& scope, AstType* ty, bool topLevel = false);
|
||||
|
||||
/**
|
||||
* Resolves a type pack from its AST annotation.
|
||||
|
|
|
@ -17,16 +17,36 @@ 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;
|
||||
// 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.
|
||||
std::vector<NotNull<const Constraint>> unsolvedConstraints;
|
||||
|
@ -37,6 +57,8 @@ 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;
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -152,6 +152,8 @@ struct Frontend
|
|||
void registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)>);
|
||||
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);
|
||||
|
||||
LoadDefinitionFileResult loadDefinitionFile(std::string_view source, const std::string& packageName);
|
||||
|
||||
NotNull<Scope> getGlobalScope();
|
||||
|
||||
private:
|
||||
|
@ -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<Scope> globalScope;
|
||||
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,7 +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, ScopePtr>> scopes; // never empty
|
||||
|
||||
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
|
||||
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
|
||||
|
|
|
@ -36,7 +36,7 @@ struct Scope
|
|||
// All the children of this scope.
|
||||
std::vector<NotNull<Scope>> children;
|
||||
std::unordered_map<Symbol, Binding> bindings;
|
||||
std::unordered_map<Name, TypeId> typeBindings;
|
||||
std::unordered_map<Name, TypeFun> typeBindings;
|
||||
std::unordered_map<Name, TypePackId> typePackBindings;
|
||||
TypePackId returnType;
|
||||
std::optional<TypePackId> varargPack;
|
||||
|
@ -52,7 +52,7 @@ struct Scope
|
|||
std::unordered_map<Name, std::unordered_map<Name, TypeFun>> importedTypeBindings;
|
||||
|
||||
std::optional<TypeId> lookup(Symbol sym);
|
||||
std::optional<TypeId> lookupTypeBinding(const Name& name);
|
||||
std::optional<TypeFun> lookupTypeBinding(const Name& name);
|
||||
std::optional<TypePackId> lookupTypePackBinding(const Name& name);
|
||||
|
||||
std::optional<TypeFun> lookupType(const Name& name);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
@ -773,7 +773,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||
PROP(indexer);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void write(struct AstTableIndexer* indexer)
|
||||
{
|
||||
if (indexer)
|
||||
|
@ -1178,7 +1178,6 @@ struct AstJsonEncoder : public AstVisitor
|
|||
write("location", comment.location);
|
||||
popComma(c);
|
||||
writeRaw("}");
|
||||
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -70,11 +70,11 @@ void ConstraintGraphBuilder::visit(AstStatBlock* 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(scope, block);
|
||||
}
|
||||
|
@ -89,6 +89,53 @@ void ConstraintGraphBuilder::visitBlockWithoutChildScope(const ScopePtr& 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);
|
||||
}
|
||||
|
@ -117,6 +164,12 @@ void ConstraintGraphBuilder::visit(const ScopePtr& 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);
|
||||
}
|
||||
|
@ -133,7 +186,7 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
|
|||
if (local->annotation)
|
||||
{
|
||||
location = local->annotation->location;
|
||||
TypeId annotation = resolveType(scope, local->annotation);
|
||||
TypeId annotation = resolveType(scope, local->annotation, /* topLevel */ true);
|
||||
addConstraint(scope, SubtypeConstraint{ty, annotation});
|
||||
}
|
||||
|
||||
|
@ -171,11 +224,10 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
|
|||
|
||||
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
|
||||
{
|
||||
auto checkNumber = [&](AstExpr* expr)
|
||||
{
|
||||
auto checkNumber = [&](AstExpr* expr) {
|
||||
if (!expr)
|
||||
return;
|
||||
|
||||
|
||||
TypeId t = check(scope, expr);
|
||||
addConstraint(scope, SubtypeConstraint{t, singletonTypes.numberType});
|
||||
};
|
||||
|
@ -307,19 +359,6 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
|
|||
{
|
||||
ScopePtr innerScope = childScope(block->location, scope);
|
||||
|
||||
// In order to enable mutually-recursive type aliases, we need to
|
||||
// populate the type bindings before we actually check any of the
|
||||
// alias statements. Since we're not ready to actually resolve
|
||||
// any of the annotations, we just use a fresh type for now.
|
||||
for (AstStat* stat : block->body)
|
||||
{
|
||||
if (auto alias = stat->as<AstStatTypeAlias>())
|
||||
{
|
||||
TypeId initialType = freshType(scope);
|
||||
scope->typeBindings[alias->name.value] = initialType;
|
||||
}
|
||||
}
|
||||
|
||||
visitBlockWithoutChildScope(innerScope, block);
|
||||
}
|
||||
|
||||
|
@ -348,29 +387,48 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatIf* ifStatement
|
|||
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});
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -707,7 +765,7 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
|||
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)
|
||||
|
@ -745,7 +803,7 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
|
|||
|
||||
if (local->annotation)
|
||||
{
|
||||
TypeId argAnnotation = resolveType(signatureScope, local->annotation);
|
||||
TypeId argAnnotation = resolveType(signatureScope, local->annotation, /* topLevel */ true);
|
||||
addConstraint(signatureScope, SubtypeConstraint{t, argAnnotation});
|
||||
}
|
||||
}
|
||||
|
@ -784,20 +842,65 @@ void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFun
|
|||
}
|
||||
}
|
||||
|
||||
TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
|
||||
TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty, bool topLevel)
|
||||
{
|
||||
TypeId result = nullptr;
|
||||
|
||||
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>())
|
||||
{
|
||||
|
@ -846,7 +949,7 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
|
|||
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)
|
||||
|
@ -956,7 +1059,15 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTyp
|
|||
}
|
||||
else if (auto gen = tp->as<AstTypePackGeneric>())
|
||||
{
|
||||
result = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), gen->genericName.value}});
|
||||
if (std::optional<TypePackId> lookup = scope->lookupTypePackBinding(gen->genericName.value))
|
||||
{
|
||||
result = *lookup;
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(tp->location, UnknownSymbol{gen->genericName.value, UnknownSymbol::Context::Type});
|
||||
result = singletonTypes.errorRecoveryTypePack();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
@ -37,6 +39,170 @@ static void dumpConstraints(NotNull<Scope> scope, ToStringOptions& opts)
|
|||
dumpConstraints(child, opts);
|
||||
}
|
||||
|
||||
static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
|
||||
const TypeFun& fn, const std::vector<TypeId>& rawTypeArguments, const std::vector<TypePackId>& rawPackArguments, TypeArena* arena)
|
||||
{
|
||||
std::vector<TypeId> saturatedTypeArguments;
|
||||
std::vector<TypeId> extraTypes;
|
||||
std::vector<TypePackId> saturatedPackArguments;
|
||||
|
||||
for (size_t i = 0; i < rawTypeArguments.size(); ++i)
|
||||
{
|
||||
TypeId ty = rawTypeArguments[i];
|
||||
|
||||
if (i < fn.typeParams.size())
|
||||
saturatedTypeArguments.push_back(ty);
|
||||
else
|
||||
extraTypes.push_back(ty);
|
||||
}
|
||||
|
||||
// If we collected extra types, put them in a type pack now. This case is
|
||||
// mutually exclusive with the type pack -> type conversion we do below:
|
||||
// extraTypes will only have elements in it if we have more types than we
|
||||
// have parameter slots for them to go into.
|
||||
if (!extraTypes.empty())
|
||||
{
|
||||
saturatedPackArguments.push_back(arena->addTypePack(extraTypes));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < rawPackArguments.size(); ++i)
|
||||
{
|
||||
TypePackId tp = rawPackArguments[i];
|
||||
|
||||
// If we are short on regular type saturatedTypeArguments and we have a single
|
||||
// element type pack, we can decompose that to the type it contains and
|
||||
// use that as a type parameter.
|
||||
if (saturatedTypeArguments.size() < fn.typeParams.size() && size(tp) == 1 && finite(tp) && first(tp) && saturatedPackArguments.empty())
|
||||
{
|
||||
saturatedTypeArguments.push_back(*first(tp));
|
||||
}
|
||||
else
|
||||
{
|
||||
saturatedPackArguments.push_back(tp);
|
||||
}
|
||||
}
|
||||
|
||||
size_t typesProvided = saturatedTypeArguments.size();
|
||||
size_t typesRequired = fn.typeParams.size();
|
||||
|
||||
size_t packsProvided = saturatedPackArguments.size();
|
||||
size_t packsRequired = fn.typePackParams.size();
|
||||
|
||||
// Extra types should be accumulated in extraTypes, not saturatedTypeArguments. Extra
|
||||
// packs will be accumulated in saturatedPackArguments, so we don't have an
|
||||
// assertion for that.
|
||||
LUAU_ASSERT(typesProvided <= typesRequired);
|
||||
|
||||
// If we didn't provide enough types, but we did provide a type pack, we
|
||||
// don't want to use defaults. The rationale for this is that if the user
|
||||
// provides a pack but doesn't provide enough types, we want to report an
|
||||
// error, rather than simply using the default saturatedTypeArguments, if they exist. If
|
||||
// they did provide enough types, but not enough packs, we of course want to
|
||||
// use the default packs.
|
||||
bool needsDefaults = (typesProvided < typesRequired && packsProvided == 0) || (typesProvided == typesRequired && packsProvided < packsRequired);
|
||||
|
||||
if (needsDefaults)
|
||||
{
|
||||
// Default types can reference earlier types. It's legal to write
|
||||
// something like
|
||||
// type T<A, B = A> = (A, B) -> number
|
||||
// and we need to respect that. We use an ApplyTypeFunction for this.
|
||||
ApplyTypeFunction atf{arena};
|
||||
|
||||
for (size_t i = 0; i < typesProvided; ++i)
|
||||
atf.typeArguments[fn.typeParams[i].ty] = saturatedTypeArguments[i];
|
||||
|
||||
for (size_t i = typesProvided; i < typesRequired; ++i)
|
||||
{
|
||||
TypeId defaultTy = fn.typeParams[i].defaultValue.value_or(nullptr);
|
||||
|
||||
// We will fill this in with the error type later.
|
||||
if (!defaultTy)
|
||||
break;
|
||||
|
||||
TypeId instantiatedDefault = atf.substitute(defaultTy).value_or(getSingletonTypes().errorRecoveryType());
|
||||
atf.typeArguments[fn.typeParams[i].ty] = instantiatedDefault;
|
||||
saturatedTypeArguments.push_back(instantiatedDefault);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < packsProvided; ++i)
|
||||
{
|
||||
atf.typePackArguments[fn.typePackParams[i].tp] = saturatedPackArguments[i];
|
||||
}
|
||||
|
||||
for (size_t i = packsProvided; i < packsRequired; ++i)
|
||||
{
|
||||
TypePackId defaultTp = fn.typePackParams[i].defaultValue.value_or(nullptr);
|
||||
|
||||
// We will fill this in with the error type pack later.
|
||||
if (!defaultTp)
|
||||
break;
|
||||
|
||||
TypePackId instantiatedDefault = atf.substitute(defaultTp).value_or(getSingletonTypes().errorRecoveryTypePack());
|
||||
atf.typePackArguments[fn.typePackParams[i].tp] = instantiatedDefault;
|
||||
saturatedPackArguments.push_back(instantiatedDefault);
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't create an extra type pack from overflowing parameter packs,
|
||||
// and we're still missing a type pack, plug in an empty type pack as the
|
||||
// value of the empty packs.
|
||||
if (extraTypes.empty() && saturatedPackArguments.size() + 1 == fn.typePackParams.size())
|
||||
{
|
||||
saturatedPackArguments.push_back(arena->addTypePack({}));
|
||||
}
|
||||
|
||||
// We need to have _something_ when we substitute the generic saturatedTypeArguments,
|
||||
// even if they're missing, so we use the error type as a filler.
|
||||
for (size_t i = saturatedTypeArguments.size(); i < typesRequired; ++i)
|
||||
{
|
||||
saturatedTypeArguments.push_back(getSingletonTypes().errorRecoveryType());
|
||||
}
|
||||
|
||||
for (size_t i = saturatedPackArguments.size(); i < packsRequired; ++i)
|
||||
{
|
||||
saturatedPackArguments.push_back(getSingletonTypes().errorRecoveryTypePack());
|
||||
}
|
||||
|
||||
// At this point, these two conditions should be true. If they aren't we
|
||||
// will run into access violations.
|
||||
LUAU_ASSERT(saturatedTypeArguments.size() == fn.typeParams.size());
|
||||
LUAU_ASSERT(saturatedPackArguments.size() == fn.typePackParams.size());
|
||||
|
||||
return {saturatedTypeArguments, saturatedPackArguments};
|
||||
}
|
||||
|
||||
bool InstantiationSignature::operator==(const InstantiationSignature& rhs) const
|
||||
{
|
||||
return fn == rhs.fn && arguments == rhs.arguments && packArguments == rhs.packArguments;
|
||||
}
|
||||
|
||||
size_t HashInstantiationSignature::operator()(const InstantiationSignature& signature) const
|
||||
{
|
||||
size_t hash = std::hash<TypeId>{}(signature.fn.type);
|
||||
for (const GenericTypeDefinition& p : signature.fn.typeParams)
|
||||
{
|
||||
hash ^= (std::hash<TypeId>{}(p.ty) << 1);
|
||||
}
|
||||
|
||||
for (const GenericTypePackDefinition& p : signature.fn.typePackParams)
|
||||
{
|
||||
hash ^= (std::hash<TypePackId>{}(p.tp) << 1);
|
||||
}
|
||||
|
||||
for (const TypeId a : signature.arguments)
|
||||
{
|
||||
hash ^= (std::hash<TypeId>{}(a) << 1);
|
||||
}
|
||||
|
||||
for (const TypePackId a : signature.packArguments)
|
||||
{
|
||||
hash ^= (std::hash<TypePackId>{}(a) << 1);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
void dump(NotNull<Scope> rootScope, ToStringOptions& opts)
|
||||
{
|
||||
printf("constraints:\n");
|
||||
|
@ -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 Scope* scope, ToStringOptions& opts)
|
||||
static void dumpScopeAndChildren(const Scope* scope, Json::JsonEmitter& emitter, ToStringOptions& opts)
|
||||
{
|
||||
std::string output = "{\"bindings\":{";
|
||||
emitter.writeRaw("{");
|
||||
Json::write(emitter, "bindings");
|
||||
emitter.writeRaw(":");
|
||||
|
||||
Json::ObjectEmitter o = emitter.writeObject();
|
||||
|
||||
bool comma = false;
|
||||
for (const auto& [name, binding] : scope->bindings)
|
||||
{
|
||||
if (comma)
|
||||
output += ",";
|
||||
|
||||
output += "\"";
|
||||
output += name.c_str();
|
||||
output += "\": \"";
|
||||
|
||||
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(":");
|
||||
|
||||
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 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 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()
|
||||
|
|
|
@ -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");
|
||||
|
@ -770,14 +822,7 @@ NotNull<Scope> Frontend::getGlobalScope()
|
|||
{
|
||||
if (!globalScope)
|
||||
{
|
||||
const SingletonTypes& singletonTypes = getSingletonTypes();
|
||||
|
||||
globalScope = std::make_unique<Scope>(singletonTypes.anyTypePack);
|
||||
globalScope->typeBindings["nil"] = singletonTypes.nilType;
|
||||
globalScope->typeBindings["number"] = singletonTypes.numberType;
|
||||
globalScope->typeBindings["string"] = singletonTypes.stringType;
|
||||
globalScope->typeBindings["boolean"] = singletonTypes.booleanType;
|
||||
globalScope->typeBindings["thread"] = singletonTypes.threadType;
|
||||
globalScope = typeChecker.globalScope;
|
||||
}
|
||||
|
||||
return NotNull(globalScope.get());
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -2589,6 +2590,45 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class LintIntegerParsing : AstVisitor
|
||||
{
|
||||
public:
|
||||
LUAU_NOINLINE static void process(LintContext& context)
|
||||
{
|
||||
LintIntegerParsing pass;
|
||||
pass.context = &context;
|
||||
|
||||
context.root->visit(&pass);
|
||||
}
|
||||
|
||||
private:
|
||||
LintContext* context;
|
||||
|
||||
bool visit(AstExprConstantNumber* node) override
|
||||
{
|
||||
switch (node->parseResult)
|
||||
{
|
||||
case ConstantNumberParseResult::Ok:
|
||||
case ConstantNumberParseResult::Malformed:
|
||||
break;
|
||||
case ConstantNumberParseResult::BinOverflow:
|
||||
emitWarning(*context, LintWarning::Code_IntegerParsing, node->location,
|
||||
"Binary number literal exceeded available precision and has been truncated to 2^64");
|
||||
break;
|
||||
case ConstantNumberParseResult::HexOverflow:
|
||||
emitWarning(*context, LintWarning::Code_IntegerParsing, node->location,
|
||||
"Hexadecimal number literal exceeded available precision and has been truncated to 2^64");
|
||||
break;
|
||||
case ConstantNumberParseResult::DoublePrefix:
|
||||
emitWarning(*context, LintWarning::Code_IntegerParsing, node->location,
|
||||
"Hexadecimal number literal has a double prefix, which will fail to parse in the future; remove the extra 0x to fix");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static void fillBuiltinGlobals(LintContext& context, const AstNameTable& names, const ScopePtr& env)
|
||||
{
|
||||
ScopePtr current = env;
|
||||
|
@ -2810,6 +2850,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);
|
||||
|
||||
|
@ -140,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
@ -203,16 +202,8 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,16 +214,8 @@ void quantify(TypeId ty, Scope* scope)
|
|||
|
||||
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;
|
||||
|
|
|
@ -122,7 +122,7 @@ std::optional<TypeId> Scope::lookup(Symbol sym)
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<TypeId> Scope::lookupTypeBinding(const Name& name)
|
||||
std::optional<TypeFun> Scope::lookupTypeBinding(const Name& name)
|
||||
{
|
||||
Scope* s = this;
|
||||
while (s)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -232,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;
|
||||
|
@ -410,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)
|
||||
|
@ -1459,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,6 +67,13 @@ 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)
|
||||
|
@ -363,12 +370,153 @@ struct TypeChecker2 : public AstVisitor
|
|||
bool visit(AstTypeReference* ty) override
|
||||
{
|
||||
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"
|
||||
|
@ -36,11 +37,8 @@ LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false)
|
|||
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix3, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, 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)
|
||||
|
@ -2108,35 +2106,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())
|
||||
|
@ -4770,16 +4749,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)
|
||||
{
|
||||
|
@ -5253,7 +5224,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];
|
||||
|
@ -5494,65 +5465,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)
|
||||
|
@ -1412,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
|
||||
|
|
|
@ -474,16 +474,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,7 +21,8 @@ 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)
|
||||
|
||||
bool lua_telemetry_parsed_out_of_range_bin_integer = false;
|
||||
|
@ -2032,8 +2033,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);
|
||||
|
||||
|
@ -2053,9 +2056,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";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2063,11 +2063,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])
|
||||
|
@ -2075,10 +2077,7 @@ static const char* parseNumber(double& result, const char* data)
|
|||
if (DFFlag::LuaReportParseIntegerIssues && data[2] == '0' && (data[3] == 'x' || data[3] == 'X'))
|
||||
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;
|
||||
|
@ -2090,6 +2089,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])
|
||||
{
|
||||
|
@ -2118,6 +2119,73 @@ static bool parseNumber_DEPRECATED(double& result, const char* data)
|
|||
}
|
||||
}
|
||||
|
||||
static ConstantNumberParseResult parseInteger(double& result, const char* data, int base)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauLintParseIntegerIssues);
|
||||
LUAU_ASSERT(base == 2 || base == 16);
|
||||
|
||||
char* end = nullptr;
|
||||
unsigned long long value = strtoull(data, &end, base);
|
||||
|
||||
if (*end != 0)
|
||||
return ConstantNumberParseResult::Malformed;
|
||||
|
||||
result = double(value);
|
||||
|
||||
if (value == ULLONG_MAX && errno == ERANGE)
|
||||
{
|
||||
// 'errno' might have been set before we called 'strtoull', but we don't want the overhead of resetting a TLS variable on each call
|
||||
// so we only reset it when we get a result that might be an out-of-range error and parse again to make sure
|
||||
errno = 0;
|
||||
value = strtoull(data, &end, base);
|
||||
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
if (DFFlag::LuaReportParseIntegerIssues)
|
||||
{
|
||||
if (base == 2)
|
||||
lua_telemetry_parsed_out_of_range_bin_integer = true;
|
||||
else
|
||||
lua_telemetry_parsed_out_of_range_hex_integer = true;
|
||||
}
|
||||
|
||||
return base == 2 ? ConstantNumberParseResult::BinOverflow : ConstantNumberParseResult::HexOverflow;
|
||||
}
|
||||
}
|
||||
|
||||
return ConstantNumberParseResult::Ok;
|
||||
}
|
||||
|
||||
static ConstantNumberParseResult parseNumber(double& result, const char* data)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauLintParseIntegerIssues);
|
||||
|
||||
// binary literal
|
||||
if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2])
|
||||
return parseInteger(result, data + 2, 2);
|
||||
|
||||
// hexadecimal literal
|
||||
if (data[0] == '0' && (data[1] == 'x' || data[1] == 'X') && data[2])
|
||||
{
|
||||
if (!FFlag::LuauErrorDoubleHexPrefix && data[2] == '0' && (data[3] == 'x' || data[3] == 'X'))
|
||||
{
|
||||
if (DFFlag::LuaReportParseIntegerIssues)
|
||||
lua_telemetry_parsed_double_prefix_hex_integer = true;
|
||||
|
||||
ConstantNumberParseResult parseResult = parseInteger(result, data + 2, 16);
|
||||
return parseResult == ConstantNumberParseResult::Malformed ? parseResult : ConstantNumberParseResult::DoublePrefix;
|
||||
}
|
||||
|
||||
return parseInteger(result, data, 16); // pass in '0x' prefix, it's handled by 'strtoull'
|
||||
}
|
||||
|
||||
char* end = nullptr;
|
||||
double value = strtod(data, &end);
|
||||
|
||||
result = value;
|
||||
return *end == 0 ? ConstantNumberParseResult::Ok : ConstantNumberParseResult::Malformed;
|
||||
}
|
||||
|
||||
// simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | FUNCTION body | primaryexp
|
||||
AstExpr* Parser::parseSimpleExpr()
|
||||
{
|
||||
|
@ -2158,10 +2226,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();
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -109,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)
|
||||
|
|
|
@ -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,6 +26,7 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
|||
LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileFreeReassign, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileXEQ, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -1008,9 +1009,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;
|
||||
|
||||
|
@ -1022,36 +1022,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)
|
||||
|
|
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,7 @@ 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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -59,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;
|
||||
}
|
||||
|
@ -69,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:
|
||||
|
@ -129,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);
|
||||
|
@ -219,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;
|
||||
}
|
||||
|
@ -267,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--;
|
||||
|
@ -429,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;
|
||||
|
@ -660,7 +660,7 @@ void lua_pushcclosurek(lua_State* L, lua_CFunction fn, const char* debugname, in
|
|||
|
||||
void lua_pushboolean(lua_State* L, int b)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -829,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;
|
||||
}
|
||||
|
||||
|
@ -967,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;
|
||||
};
|
||||
|
@ -992,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);
|
||||
|
@ -1044,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;
|
||||
}
|
||||
|
@ -1084,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;
|
||||
}
|
||||
}
|
||||
|
@ -1137,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;
|
||||
}
|
||||
|
@ -1169,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;
|
||||
}
|
||||
|
||||
|
@ -1185,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;
|
||||
}
|
||||
|
||||
|
@ -1277,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);
|
||||
|
@ -1286,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);
|
||||
|
@ -1312,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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -48,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;
|
||||
}
|
||||
|
@ -81,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->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;
|
||||
}
|
||||
|
||||
|
@ -181,13 +181,13 @@ TString* luaS_newlstr(lua_State* L, const char* str, size_t l)
|
|||
{
|
||||
if (el->len == l && (memcmp(str, getstr(el), l) == 0))
|
||||
{
|
||||
/* 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)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTostringFormatSpecifier, false);
|
||||
|
||||
/* macro to `unsign' a character */
|
||||
// macro to `unsign' a character
|
||||
#define uchar(c) ((unsigned char)(c))
|
||||
|
||||
static int str_len(lua_State* L)
|
||||
|
@ -23,7 +23,7 @@ static int str_len(lua_State* L)
|
|||
|
||||
static int posrelat(int pos, size_t len)
|
||||
{
|
||||
/* relative string position: negative means back from end */
|
||||
// relative string position: negative means back from end
|
||||
if (pos < 0)
|
||||
pos += (int)len + 1;
|
||||
return (pos >= 0) ? pos : 0;
|
||||
|
@ -139,9 +139,9 @@ static int str_byte(lua_State* L)
|
|||
if ((size_t)pose > l)
|
||||
pose = (int)l;
|
||||
if (posi > pose)
|
||||
return 0; /* empty interval; return no values */
|
||||
return 0; // empty interval; return no values
|
||||
n = (int)(pose - posi + 1);
|
||||
if (posi + n <= pose) /* overflow? */
|
||||
if (posi + n <= pose) // overflow?
|
||||
luaL_error(L, "string slice too long");
|
||||
luaL_checkstack(L, n, "string slice too long");
|
||||
for (i = 0; i < n; i++)
|
||||
|
@ -151,7 +151,7 @@ static int str_byte(lua_State* L)
|
|||
|
||||
static int str_char(lua_State* L)
|
||||
{
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int n = lua_gettop(L); // number of arguments
|
||||
|
||||
luaL_Buffer b;
|
||||
char* ptr = luaL_buffinitsize(L, &b, n);
|
||||
|
@ -178,12 +178,12 @@ static int str_char(lua_State* L)
|
|||
|
||||
typedef struct MatchState
|
||||
{
|
||||
int matchdepth; /* control for recursive depth (to avoid C stack overflow) */
|
||||
const char* src_init; /* init of source string */
|
||||
const char* src_end; /* end ('\0') of source string */
|
||||
const char* p_end; /* end ('\0') of pattern */
|
||||
int matchdepth; // control for recursive depth (to avoid C stack overflow)
|
||||
const char* src_init; // init of source string
|
||||
const char* src_end; // end ('\0') of source string
|
||||
const char* p_end; // end ('\0') of pattern
|
||||
lua_State* L;
|
||||
int level; /* total number of captures (finished or unfinished) */
|
||||
int level; // total number of captures (finished or unfinished)
|
||||
struct
|
||||
{
|
||||
const char* init;
|
||||
|
@ -191,7 +191,7 @@ typedef struct MatchState
|
|||
} capture[LUA_MAXCAPTURES];
|
||||
} MatchState;
|
||||
|
||||
/* recursive function */
|
||||
// recursive function
|
||||
static const char* match(MatchState* ms, const char* s, const char* p);
|
||||
|
||||
#define L_ESC '%'
|
||||
|
@ -229,11 +229,11 @@ static const char* classend(MatchState* ms, const char* p)
|
|||
if (*p == '^')
|
||||
p++;
|
||||
do
|
||||
{ /* look for a `]' */
|
||||
{ // look for a `]'
|
||||
if (p == ms->p_end)
|
||||
luaL_error(ms->L, "malformed pattern (missing ']')");
|
||||
if (*(p++) == L_ESC && p < ms->p_end)
|
||||
p++; /* skip escapes (e.g. `%]') */
|
||||
p++; // skip escapes (e.g. `%]')
|
||||
} while (*p != ']');
|
||||
return p + 1;
|
||||
}
|
||||
|
@ -281,7 +281,7 @@ static int match_class(int c, int cl)
|
|||
break;
|
||||
case 'z':
|
||||
res = (c == 0);
|
||||
break; /* deprecated option */
|
||||
break; // deprecated option
|
||||