// See the file "COPYING" in the main distribution directory for copyright. // Class for generating identifier definition information by traversing // a function body's AST. #pragma once #include "zeek/script_opt/IDOptInfo.h" #include "zeek/script_opt/ProfileFunc.h" namespace zeek::detail { class GenIDDefs : public TraversalCallback { public: GenIDDefs(std::shared_ptr _pf, const FuncPtr& f, ScopePtr scope, StmtPtr body); private: // Traverses the given function body, using the first two // arguments for context. void TraverseFunction(const FuncPtr& f, ScopePtr scope, StmtPtr body); TraversalCode PreStmt(const Stmt*) override; void AnalyzeSwitch(const SwitchStmt* sw); TraversalCode PostStmt(const Stmt*) override; TraversalCode PreExpr(const Expr*) override; TraversalCode PostExpr(const Expr*) override; // Analyzes the target of an assignment. Returns true if the LHS // was an expression for which we can track it as a definition // (e.g., assignments to variables, but not to elements of // aggregates). "rhs" gives the expression used for simple direct // assignments. bool CheckLHS(const ExprPtr& lhs, const ExprPtr& rhs = nullptr); // True if the given expression directly represents an aggregate. bool IsAggr(const ExprPtr& e) const { return IsAggr(e.get()); } bool IsAggr(const Expr* e) const; // If -u is active, checks for whether the given identifier present // in the given expression is undefined at that point. void CheckVarUsage(const Expr* e, const IDPtr& id); // Begin a new confluence block with the given statement. void StartConfluenceBlock(const Stmt* s); // Finish up the current confluence block. If no_orig_flow is true, // then there's no control flow from the origin (the statement that // starts the block). void EndConfluenceBlock(bool no_orig_flow = false); // Note branches from the given "from" statement back up to the // beginning of, or just past, the "to" statement. If "close_all" // is true then the nature of the branch is that it terminates // all pending confluence blocks. void BranchBackTo(const Stmt* from, const Stmt* to, bool close_all); void BranchBeyond(const Stmt* from, const Stmt* to, bool close_all); // These search back through the active confluence blocks looking // for either the innermost loop, or the innermost block for which // a "break" would target going beyond that block. const Stmt* FindLoop(); const Stmt* FindBreakTarget(); // Note that the given statement executes a "return" (which could // instead be an outer "break" for a hook). void ReturnAt(const Stmt* s); // Tracks that the given identifier is defined at the current // statement in the current confluence block. 'e' is the // expression used to define the identifier, for simple direct // assignments. void TrackID(const IDPtr& id, const ExprPtr& e = nullptr); // Profile for the function. Currently, all we actually need from // this is the list of globals and locals. std::shared_ptr pf; // Whether the Func is an event/hook/function. We currently only // need to know whether it's a hook, so we correctly interpret an // outer "break" in that context. FunctionFlavor func_flavor; // The most recently traversed statement. const Stmt* last_stmt_traversed = nullptr; // Used to number Stmt objects found during AST traversal. int stmt_num; // A stack of confluence blocks, with the innermost at the top/back. std::vector confluence_blocks; // Stack of confluence blocks corresponding to activate catch-return // statements. We used to stop identifier definitions at these // boundaries, but given there's limited confluence (i.e., the body of // the catch-return *will* execute, up through its first return), we // can do broader optimization if we don't treat them as their own // (new) confluence blocks. std::vector cr_active; // The following is parallel to confluence_blocks except // the front entry tracks identifiers at the outermost // (non-confluence) scope. Thus, to index it for a given // confluence block i, we need to use i+1. std::vector modified_IDs; // If non-zero, indicates we should suspend any generation // of usage errors. A counter rather than a boolean because // such situations might nest. int suppress_usage = 0; }; } // namespace zeek::detail