luau/Analysis/src/LValue.cpp

108 lines
3.0 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/LValue.h"
#include "Luau/Ast.h"
#include <vector>
namespace Luau
{
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.
}
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))
if (auto string = indexexpr->index->as<AstExprConstantString>())
return Field{std::make_shared<LValue>(*lvalue), std::string(string->value.data, string->value.size)};
}
return std::nullopt;
}
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;
}
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f)
{
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;
}
}
void addRefinement(RefinementMap& refis, const LValue& lvalue, TypeId ty)
{
refis[lvalue] = ty;
}
} // namespace Luau