2021-10-29 21:25:12 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
2022-01-06 23:26:14 +00:00
|
|
|
#include "Luau/LValue.h"
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
#include "Luau/Ast.h"
|
|
|
|
|
2022-01-06 23:26:14 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
2022-01-06 23:26:14 +00:00
|
|
|
bool Field::operator==(const Field& rhs) const
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(parent && rhs.parent);
|
|
|
|
return key == rhs.key && (parent == rhs.parent || *parent == *rhs.parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Field::operator!=(const Field& rhs) const
|
|
|
|
{
|
|
|
|
return !(*this == rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t LValueHasher::operator()(const LValue& lvalue) const
|
|
|
|
{
|
|
|
|
// Most likely doesn't produce high quality hashes, but we're probably ok enough with it.
|
|
|
|
// When an evidence is shown that operator==(LValue) is used more often than it should, we can have a look at improving the hash quality.
|
|
|
|
size_t acc = 0;
|
|
|
|
size_t offset = 0;
|
|
|
|
|
|
|
|
const LValue* current = &lvalue;
|
|
|
|
while (current)
|
|
|
|
{
|
|
|
|
if (auto field = get<Field>(*current))
|
|
|
|
acc ^= (std::hash<std::string>{}(field->key) << 1) >> ++offset;
|
|
|
|
else if (auto symbol = get<Symbol>(*current))
|
|
|
|
acc ^= std::hash<Symbol>{}(*symbol) << 1;
|
|
|
|
else
|
|
|
|
LUAU_ASSERT(!"Hash not accumulated for this new LValue alternative.");
|
|
|
|
|
|
|
|
current = baseof(*current);
|
|
|
|
}
|
|
|
|
|
|
|
|
return acc;
|
|
|
|
}
|
|
|
|
|
|
|
|
const LValue* baseof(const LValue& lvalue)
|
|
|
|
{
|
|
|
|
if (auto field = get<Field>(lvalue))
|
|
|
|
return field->parent.get();
|
|
|
|
|
|
|
|
auto symbol = get<Symbol>(lvalue);
|
|
|
|
LUAU_ASSERT(symbol);
|
|
|
|
return nullptr; // Base of root is null.
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
std::optional<LValue> tryGetLValue(const AstExpr& node)
|
|
|
|
{
|
|
|
|
const AstExpr* expr = &node;
|
|
|
|
while (auto e = expr->as<AstExprGroup>())
|
|
|
|
expr = e->expr;
|
|
|
|
|
|
|
|
if (auto local = expr->as<AstExprLocal>())
|
|
|
|
return Symbol{local->local};
|
|
|
|
else if (auto global = expr->as<AstExprGlobal>())
|
|
|
|
return Symbol{global->name};
|
|
|
|
else if (auto indexname = expr->as<AstExprIndexName>())
|
|
|
|
{
|
|
|
|
if (auto lvalue = tryGetLValue(*indexname->expr))
|
|
|
|
return Field{std::make_shared<LValue>(*lvalue), indexname->index.value};
|
|
|
|
}
|
|
|
|
else if (auto indexexpr = expr->as<AstExprIndexExpr>())
|
|
|
|
{
|
|
|
|
if (auto lvalue = tryGetLValue(*indexexpr->expr))
|
2021-12-10 22:05:05 +00:00
|
|
|
if (auto string = indexexpr->index->as<AstExprConstantString>())
|
2021-10-29 21:25:12 +01:00
|
|
|
return Field{std::make_shared<LValue>(*lvalue), std::string(string->value.data, string->value.size)};
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2022-04-15 00:57:43 +01:00
|
|
|
Symbol getBaseSymbol(const LValue& lvalue)
|
|
|
|
{
|
|
|
|
const LValue* current = &lvalue;
|
|
|
|
while (auto field = get<Field>(*current))
|
|
|
|
current = baseof(*current);
|
|
|
|
|
|
|
|
const Symbol* symbol = get<Symbol>(*current);
|
|
|
|
LUAU_ASSERT(symbol);
|
|
|
|
return *symbol;
|
|
|
|
}
|
|
|
|
|
2022-02-04 16:45:57 +00:00
|
|
|
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f)
|
2022-01-06 23:26:14 +00:00
|
|
|
{
|
|
|
|
for (const auto& [k, a] : r)
|
|
|
|
{
|
|
|
|
if (auto it = l.find(k); it != l.end())
|
|
|
|
l[k] = f(it->second, a);
|
|
|
|
else
|
|
|
|
l[k] = a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
void addRefinement(RefinementMap& refis, const LValue& lvalue, TypeId ty)
|
|
|
|
{
|
2022-02-04 16:45:57 +00:00
|
|
|
refis[lvalue] = ty;
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Luau
|