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
|
|
|
|
#include "Luau/TypeUtils.h"
|
|
|
|
|
2021-11-05 02:34:35 +00:00
|
|
|
#include "Luau/Scope.h"
|
2021-10-29 21:25:12 +01:00
|
|
|
#include "Luau/ToString.h"
|
|
|
|
#include "Luau/TypeInfer.h"
|
|
|
|
|
2022-02-18 01:18:01 +00:00
|
|
|
LUAU_FASTFLAGVARIABLE(LuauTerminateCyclicMetatableIndexLookup, false)
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
|
|
|
std::optional<TypeId> findMetatableEntry(ErrorVec& errors, const ScopePtr& globalScope, TypeId type, std::string entry, Location location)
|
|
|
|
{
|
|
|
|
type = follow(type);
|
|
|
|
|
|
|
|
std::optional<TypeId> metatable = getMetatable(type);
|
|
|
|
if (!metatable)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
TypeId unwrapped = follow(*metatable);
|
|
|
|
|
|
|
|
if (get<AnyTypeVar>(unwrapped))
|
2021-12-10 22:05:05 +00:00
|
|
|
return getSingletonTypes().anyType;
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
const TableTypeVar* mtt = getTableType(unwrapped);
|
|
|
|
if (!mtt)
|
|
|
|
{
|
|
|
|
errors.push_back(TypeError{location, GenericError{"Metatable was not a table."}});
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto it = mtt->props.find(entry);
|
|
|
|
if (it != mtt->props.end())
|
|
|
|
return it->second.type;
|
|
|
|
else
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, const ScopePtr& globalScope, TypeId ty, Name name, Location location)
|
|
|
|
{
|
|
|
|
if (get<AnyTypeVar>(ty))
|
|
|
|
return ty;
|
|
|
|
|
|
|
|
if (const TableTypeVar* tableType = getTableType(ty))
|
|
|
|
{
|
|
|
|
const auto& it = tableType->props.find(name);
|
|
|
|
if (it != tableType->props.end())
|
|
|
|
return it->second.type;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<TypeId> mtIndex = findMetatableEntry(errors, globalScope, ty, "__index", location);
|
2022-02-18 01:18:01 +00:00
|
|
|
int count = 0;
|
2021-10-29 21:25:12 +01:00
|
|
|
while (mtIndex)
|
|
|
|
{
|
|
|
|
TypeId index = follow(*mtIndex);
|
2022-02-18 01:18:01 +00:00
|
|
|
|
|
|
|
if (FFlag::LuauTerminateCyclicMetatableIndexLookup)
|
|
|
|
{
|
|
|
|
if (count >= 100)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
if (const auto& itt = getTableType(index))
|
|
|
|
{
|
|
|
|
const auto& fit = itt->props.find(name);
|
|
|
|
if (fit != itt->props.end())
|
|
|
|
return fit->second.type;
|
|
|
|
}
|
|
|
|
else if (const auto& itf = get<FunctionTypeVar>(index))
|
|
|
|
{
|
|
|
|
std::optional<TypeId> r = first(follow(itf->retType));
|
|
|
|
if (!r)
|
2021-12-10 22:05:05 +00:00
|
|
|
return getSingletonTypes().nilType;
|
2021-10-29 21:25:12 +01:00
|
|
|
else
|
|
|
|
return *r;
|
|
|
|
}
|
|
|
|
else if (get<AnyTypeVar>(index))
|
2021-12-10 22:05:05 +00:00
|
|
|
return getSingletonTypes().anyType;
|
2021-10-29 21:25:12 +01:00
|
|
|
else
|
|
|
|
errors.push_back(TypeError{location, GenericError{"__index should either be a function or table. Got " + toString(index)}});
|
|
|
|
|
|
|
|
mtIndex = findMetatableEntry(errors, globalScope, *mtIndex, "__index", location);
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Luau
|