2022-10-21 18:33:43 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
// Do not include LValue. It should never be used here.
|
|
|
|
#include "Luau/Ast.h"
|
|
|
|
#include "Luau/DenseHash.h"
|
|
|
|
#include "Luau/Def.h"
|
|
|
|
#include "Luau/Symbol.h"
|
|
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
|
|
|
struct DataFlowGraph
|
|
|
|
{
|
|
|
|
DataFlowGraph(DataFlowGraph&&) = default;
|
|
|
|
DataFlowGraph& operator=(DataFlowGraph&&) = default;
|
|
|
|
|
|
|
|
// TODO: AstExprLocal, AstExprGlobal, and AstLocal* are guaranteed never to return nullopt.
|
|
|
|
// We leave them to return an optional as we build it out, but the end state is for them to return a non-optional DefId.
|
|
|
|
std::optional<DefId> getDef(const AstExpr* expr) const;
|
|
|
|
std::optional<DefId> getDef(const AstLocal* local) const;
|
|
|
|
|
|
|
|
/// Retrieve the Def that corresponds to the given Symbol.
|
|
|
|
///
|
|
|
|
/// We do not perform dataflow analysis on globals, so this function always
|
|
|
|
/// yields nullopt when passed a global Symbol.
|
|
|
|
std::optional<DefId> getDef(const Symbol& symbol) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
DataFlowGraph() = default;
|
|
|
|
|
|
|
|
DataFlowGraph(const DataFlowGraph&) = delete;
|
|
|
|
DataFlowGraph& operator=(const DataFlowGraph&) = delete;
|
|
|
|
|
|
|
|
DefArena arena;
|
|
|
|
DenseHashMap<const AstExpr*, const Def*> astDefs{nullptr};
|
|
|
|
DenseHashMap<const AstLocal*, const Def*> localDefs{nullptr};
|
|
|
|
|
|
|
|
friend struct DataFlowGraphBuilder;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DfgScope
|
|
|
|
{
|
|
|
|
DfgScope* parent;
|
|
|
|
DenseHashMap<Symbol, const Def*> bindings{Symbol{}};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ExpressionFlowGraph
|
|
|
|
{
|
|
|
|
std::optional<DefId> def;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Currently unsound. We do not presently track the control flow of the program.
|
|
|
|
// Additionally, we do not presently track assignments.
|
|
|
|
struct DataFlowGraphBuilder
|
|
|
|
{
|
|
|
|
static DataFlowGraph build(AstStatBlock* root, NotNull<struct InternalErrorReporter> handle);
|
|
|
|
|
|
|
|
private:
|
|
|
|
DataFlowGraphBuilder() = default;
|
|
|
|
|
|
|
|
DataFlowGraphBuilder(const DataFlowGraphBuilder&) = delete;
|
|
|
|
DataFlowGraphBuilder& operator=(const DataFlowGraphBuilder&) = delete;
|
|
|
|
|
|
|
|
DataFlowGraph graph;
|
|
|
|
NotNull<DefArena> arena{&graph.arena};
|
|
|
|
struct InternalErrorReporter* handle;
|
|
|
|
std::vector<std::unique_ptr<DfgScope>> scopes;
|
|
|
|
|
2022-12-02 10:46:05 +00:00
|
|
|
// Does not belong in DataFlowGraphBuilder, but the old solver allows properties to escape the scope they were defined in,
|
|
|
|
// so we will need to be able to emulate this same behavior here too. We can kill this once we have better flow sensitivity.
|
|
|
|
DenseHashMap<const Def*, std::unordered_map<std::string, const Def*>> props{nullptr};
|
|
|
|
|
2022-10-21 18:33:43 +01:00
|
|
|
DfgScope* childScope(DfgScope* scope);
|
|
|
|
|
|
|
|
std::optional<DefId> use(DfgScope* scope, Symbol symbol, AstExpr* e);
|
2022-12-02 10:46:05 +00:00
|
|
|
DefId use(DefId def, AstExprIndexName* e);
|
2022-10-21 18:33:43 +01:00
|
|
|
|
|
|
|
void visit(DfgScope* scope, AstStatBlock* b);
|
|
|
|
void visitBlockWithoutChildScope(DfgScope* scope, AstStatBlock* b);
|
|
|
|
|
|
|
|
// TODO: visit type aliases
|
|
|
|
void visit(DfgScope* scope, AstStat* s);
|
|
|
|
void visit(DfgScope* scope, AstStatIf* i);
|
|
|
|
void visit(DfgScope* scope, AstStatWhile* w);
|
|
|
|
void visit(DfgScope* scope, AstStatRepeat* r);
|
|
|
|
void visit(DfgScope* scope, AstStatBreak* b);
|
|
|
|
void visit(DfgScope* scope, AstStatContinue* c);
|
|
|
|
void visit(DfgScope* scope, AstStatReturn* r);
|
|
|
|
void visit(DfgScope* scope, AstStatExpr* e);
|
|
|
|
void visit(DfgScope* scope, AstStatLocal* l);
|
|
|
|
void visit(DfgScope* scope, AstStatFor* f);
|
|
|
|
void visit(DfgScope* scope, AstStatForIn* f);
|
|
|
|
void visit(DfgScope* scope, AstStatAssign* a);
|
|
|
|
void visit(DfgScope* scope, AstStatCompoundAssign* c);
|
|
|
|
void visit(DfgScope* scope, AstStatFunction* f);
|
|
|
|
void visit(DfgScope* scope, AstStatLocalFunction* l);
|
|
|
|
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExpr* e);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprLocal* l);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprGlobal* g);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprCall* c);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprIndexName* i);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprIndexExpr* i);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprFunction* f);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprTable* t);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprUnary* u);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprBinary* b);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprTypeAssertion* t);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprIfElse* i);
|
|
|
|
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprInterpString* i);
|
|
|
|
|
|
|
|
// TODO: visitLValue
|
|
|
|
// TODO: visitTypes (because of typeof which has access to values namespace, needs unreachable scope)
|
|
|
|
// TODO: visitTypePacks (because of typeof which has access to values namespace, needs unreachable scope)
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace Luau
|