zeek/src/Stmt.h

779 lines
23 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
// Zeek statements.
#include "zeek/Dict.h"
#include "zeek/Expr.h"
#include "zeek/ID.h"
#include "zeek/StmtBase.h"
#include "zeek/Type.h"
#include "zeek/ZeekList.h"
namespace zeek::detail {
class CompositeHash;
class NameExpr;
using NameExprPtr = IntrusivePtr<zeek::detail::NameExpr>;
class ZAMCompiler; // for "friend" declarations
class ExprListStmt : public Stmt {
public:
const ListExpr* ExprList() const { return l.get(); }
const ListExprPtr& ExprListPtr() const { return l; }
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
void Inline(Inliner* inl) override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
protected:
ExprListStmt(StmtTag t, ListExprPtr arg_l);
~ExprListStmt() override;
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
virtual ValPtr DoExec(std::vector<ValPtr> vals, StmtFlowType& flow) = 0;
void StmtDescribe(ODesc* d) const override;
ListExprPtr l;
// Optimization-related:
// Returns a new version of the original derived object
// based on the given list of singleton expressions.
virtual StmtPtr DoSubclassReduce(ListExprPtr singletons, Reducer* c) = 0;
};
class PrintStmt final : public ExprListStmt {
public:
template<typename L>
explicit PrintStmt(L&& l) : ExprListStmt(STMT_PRINT, std::forward<L>(l)) {}
// Optimization-related:
StmtPtr Duplicate() override;
protected:
ValPtr DoExec(std::vector<ValPtr> vals, StmtFlowType& flow) override;
// Optimization-related:
StmtPtr DoSubclassReduce(ListExprPtr singletons, Reducer* c) override;
};
extern void do_print_stmt(const std::vector<ValPtr>& vals);
class ExprStmt : public Stmt {
public:
explicit ExprStmt(ExprPtr e);
~ExprStmt() override;
// This constructor is only meant for internal use, but it's
// not protected since ExprPtr's mask the actual caller,
// not allowing us to use "friend" for protected access.
ExprStmt(StmtTag t, ExprPtr e);
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
const Expr* StmtExpr() const { return e.get(); }
ExprPtr StmtExprPtr() const;
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
void Inline(Inliner* inl) override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
protected:
virtual ValPtr DoExec(Frame* f, Val* v, StmtFlowType& flow);
bool IsPure() const override;
ExprPtr e;
};
class IfStmt final : public ExprStmt {
public:
IfStmt(ExprPtr test, StmtPtr s1, StmtPtr s2);
~IfStmt() override;
const Stmt* TrueBranch() const { return s1.get(); }
const Stmt* FalseBranch() const { return s2.get(); }
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
void Inline(Inliner* inl) override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
bool NoFlowAfter(bool ignore_break) const override;
bool CouldReturn(bool ignore_break) const override;
protected:
ValPtr DoExec(Frame* f, Val* v, StmtFlowType& flow) override;
bool IsPure() const override;
StmtPtr s1;
StmtPtr s2;
};
class Case final : public Obj {
public:
Case(ListExprPtr c, IDPList* types, StmtPtr arg_s);
~Case() override;
const ListExpr* ExprCases() const { return expr_cases.get(); }
ListExpr* ExprCases() { return expr_cases.get(); }
const IDPList* TypeCases() const { return type_cases; }
IDPList* TypeCases() { return type_cases; }
const Stmt* Body() const { return s.get(); }
Stmt* Body() { return s.get(); }
void UpdateBody(StmtPtr new_body) { s = std::move(new_body); }
void Describe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const;
// Optimization-related:
IntrusivePtr<Case> Duplicate();
protected:
ListExprPtr expr_cases;
IDPList* type_cases;
StmtPtr s;
};
using case_list = PList<Case>;
class SwitchStmt final : public ExprStmt {
public:
SwitchStmt(ExprPtr index, case_list* cases);
~SwitchStmt() override;
const case_list* Cases() const { return cases; }
bool HasDefault() const { return default_case_idx != -1; }
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
void Inline(Inliner* inl) override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
bool NoFlowAfter(bool ignore_break) const override;
bool CouldReturn(bool ignore_break) const override;
protected:
friend class ZAMCompiler;
friend class CPPCompile;
int DefaultCaseIndex() const { return default_case_idx; }
const auto& ValueMap() const { return case_label_value_map; }
const std::vector<std::pair<ID*, int>>* TypeMap() const { return &case_label_type_list; }
const CompositeHash* CompHash() const { return comp_hash; }
ValPtr DoExec(Frame* f, Val* v, StmtFlowType& flow) override;
bool IsPure() const override;
// Initialize composite hash and case label map.
void Init();
// Adds entries in case_label_value_map and case_label_hash_map
// for the given value to associate it with the given index in
// the cases list. If the entry already exists, returns false,
// else returns true.
bool AddCaseLabelValueMapping(const Val* v, int idx);
// Adds an entry in case_label_type_map for the given type (w/ ID) to
// associate it with the given index in the cases list. If an entry
// for the type already exists, returns false; else returns true.
bool AddCaseLabelTypeMapping(ID* t, int idx);
// Returns index of a case label that matches the value, or
// default_case_idx if no case label matches (which may be -1 if
// there's no default label). The second tuple element is the ID of
// the matching type-based case if it defines one.
std::pair<int, ID*> FindCaseLabelMatch(const Val* v) const;
case_list* cases;
int default_case_idx;
CompositeHash* comp_hash;
std::unordered_map<const Val*, int> case_label_value_map;
PDict<int> case_label_hash_map;
std::vector<std::pair<ID*, int>> case_label_type_list;
};
// Helper class. Added for script optimization, but it makes sense
// in terms of factoring even without.
class AddDelStmt : public ExprStmt {
public:
TraversalCode Traverse(TraversalCallback* cb) const override;
bool IsPure() const override;
// Optimization-related:
StmtPtr DoReduce(Reducer* c) override;
bool IsReduced(Reducer* c) const override;
protected:
AddDelStmt(StmtTag t, ExprPtr arg_e);
};
class AddStmt final : public AddDelStmt {
public:
explicit AddStmt(ExprPtr e);
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
// Optimization-related:
StmtPtr Duplicate() override;
};
class DelStmt final : public AddDelStmt {
public:
explicit DelStmt(ExprPtr e);
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
// Optimization-related:
StmtPtr Duplicate() override;
};
class EventStmt final : public ExprStmt {
public:
explicit EventStmt(EventExprPtr e);
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
StmtPtr DoReduce(Reducer* c) override;
protected:
EventExprPtr event_expr;
};
class WhileStmt final : public Stmt {
public:
WhileStmt(ExprPtr loop_condition, StmtPtr body);
~WhileStmt() override;
bool IsPure() const override;
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
void Inline(Inliner* inl) override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
const ExprPtr& Condition() const { return loop_condition; }
StmtPtr CondPredStmt() const { return loop_cond_pred_stmt; }
const StmtPtr& Body() const { return body; }
const StmtPtr& ConditionAsStmt() const { return stmt_loop_condition; }
// Note, no need for a NoFlowAfter method because the loop might
// execute zero times, so it's always the default of "false".
// However, we do need to check for potential returns.
bool CouldReturn(bool ignore_break) const override;
protected:
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
ExprPtr loop_condition;
StmtPtr body;
// Optimization-related member variables.
// When in reduced form, the following holds a statement (which
// might be a block) that's a *predecessor* necessary for evaluating
// the loop's conditional.
StmtPtr loop_cond_pred_stmt = nullptr;
// When reducing, we create a *statement* associated with
// evaluating the reduced conditional, as well as the reduced
// expression. This turns out to be useful in propagating RDs/UDs.
StmtPtr stmt_loop_condition = nullptr;
};
class ForStmt final : public ExprStmt {
public:
ForStmt(IDPList* loop_vars, ExprPtr loop_expr);
// Special constructor for key value for loop.
ForStmt(IDPList* loop_vars, ExprPtr loop_expr, IDPtr val_var);
~ForStmt() override;
void AddBody(StmtPtr arg_body) { body = std::move(arg_body); }
const IDPList* LoopVars() const { return loop_vars; }
IDPtr ValueVar() const { return value_var; }
const Expr* LoopExpr() const { return e.get(); }
const Stmt* LoopBody() const { return body.get(); }
bool IsPure() const override;
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
void Inline(Inliner* inl) override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
// Note, no need for a NoFlowAfter method because the loop might
// execute zero times, so it's always the default of "false".
// However, we do need to check for potential returns.
bool CouldReturn(bool ignore_break) const override;
protected:
ValPtr DoExec(Frame* f, Val* v, StmtFlowType& flow) override;
IDPList* loop_vars;
StmtPtr body;
// Stores the value variable being used for a key value for loop.
// Always set to nullptr unless special constructor is called.
IDPtr value_var;
};
class NextStmt final : public Stmt {
public:
NextStmt() : Stmt(STMT_NEXT) {}
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
bool IsPure() const override;
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override { return SetSucc(new NextStmt()); }
bool NoFlowAfter(bool ignore_break) const override { return true; }
protected:
};
class BreakStmt final : public Stmt {
public:
BreakStmt() : Stmt(STMT_BREAK) {}
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
bool IsPure() const override;
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override { return SetSucc(new BreakStmt()); }
bool NoFlowAfter(bool ignore_break) const override { return ! ignore_break; }
bool CouldReturn(bool ignore_break) const override { return ! ignore_break; }
protected:
};
class FallthroughStmt final : public Stmt {
public:
FallthroughStmt() : Stmt(STMT_FALLTHROUGH) {}
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
bool IsPure() const override;
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override { return SetSucc(new FallthroughStmt()); }
protected:
};
class ReturnStmt final : public ExprStmt {
public:
explicit ReturnStmt(ExprPtr e);
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
void StmtDescribe(ODesc* d) const override;
// Optimization-related:
StmtPtr Duplicate() override;
// Constructor used internally, for when we've already done
// all of the type-checking.
ReturnStmt(ExprPtr e, bool ignored);
// Optimization-related:
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
bool NoFlowAfter(bool ignore_break) const override { return true; }
bool CouldReturn(bool ignore_break) const override { return true; }
};
class StmtList : public Stmt {
public:
StmtList();
~StmtList() override = default;
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
const auto& Stmts() const { return stmts; }
auto& Stmts() { return stmts; }
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
void Inline(Inliner* inl) override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
bool NoFlowAfter(bool ignore_break) const override;
bool CouldReturn(bool ignore_break) const override;
// Idioms commonly used in reduction.
StmtList(StmtPtr s1, StmtPtr s2);
StmtList(StmtPtr s1, StmtPtr s2, StmtPtr s3);
protected:
bool IsPure() const override;
std::vector<StmtPtr> stmts;
// Optimization-related:
bool ReduceStmt(unsigned int& s_i, std::vector<StmtPtr>& f_stmts, Reducer* c);
void ResetStmts(std::vector<StmtPtr> new_stmts) { stmts = std::move(new_stmts); }
};
class InitStmt final : public Stmt {
public:
explicit InitStmt(std::vector<IDPtr> arg_inits);
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
const std::vector<IDPtr>& Inits() const { return inits; }
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
protected:
std::vector<IDPtr> inits;
};
class NullStmt final : public Stmt {
public:
NullStmt(bool arg_is_directive = false);
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
bool IsPure() const override;
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override { return SetSucc(new NullStmt()); }
// Returns true if this NullStmt represents a directive (@if..., @else, @endif)
bool IsDirective() const { return is_directive; };
private:
bool is_directive;
};
class AssertStmt final : public Stmt {
public:
explicit AssertStmt(ExprPtr cond, ExprPtr msg = nullptr);
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
const auto& Cond() const { return cond; }
const auto& CondDesc() const { return cond_desc; }
const auto& Msg() const { return msg; }
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
private:
ExprPtr cond;
std::string cond_desc;
ExprPtr msg;
};
// Helper function for reporting on asserts that either failed, or should
// be processed regardless due to the presence of a "assertion_result" hook.
//
// If "cond" is false, throws an InterpreterException after reporting.
extern void report_assert(bool cond, std::string_view cond_desc, StringValPtr msg_val, const Location* loc);
// A helper class for tracking all of the information associated with
// a "when" statement, and constructing the necessary components in support
// of lambda-style captures.
class WhenInfo {
public:
// Takes ownership of the CaptureList.
WhenInfo(ExprPtr cond, FuncType::CaptureList* cl, bool is_return);
// Used for duplication to support inlining.
WhenInfo(const WhenInfo* orig);
// Constructor used by script optimization to create a stub.
WhenInfo(bool is_return);
~WhenInfo() { delete cl; }
void AddBody(StmtPtr arg_s) { s = std::move(arg_s); }
void AddTimeout(ExprPtr arg_timeout, StmtPtr arg_timeout_s) {
timeout = std::move(arg_timeout);
timeout_s = std::move(arg_timeout_s);
}
// Complete construction of the associated internals, including
// the (complex) lambda used to access the different elements of
// the statement. The optional argument is only for generating
// error messages.
void Build(StmtPtr ws = nullptr);
// This is available after a call to Build().
const LambdaExprPtr& Lambda() const { return lambda; }
// Instantiate a new instance, either by evaluating the associated
// lambda, or directly using the given function value (for compiled
// code).
void Instantiate(Frame* f);
void Instantiate(ValPtr func);
// Return the original components used to construct the "when".
const ExprPtr& OrigCond() const { return cond; }
const StmtPtr& OrigBody() const { return s; }
const ExprPtr& OrigTimeout() const { return timeout; }
const StmtPtr& OrigTimeoutStmt() const { return timeout_s; }
// Return different invocations of a lambda that manages the captures.
ExprPtr Cond();
StmtPtr WhenBody();
StmtPtr TimeoutStmt();
ExprPtr TimeoutExpr() const { return timeout; }
void SetTimeoutExpr(ExprPtr e) { timeout = std::move(e); }
double TimeoutVal(Frame* f);
FuncType::CaptureList* Captures() { return cl; }
bool IsReturn() const { return is_return; }
// The locals and globals used in the conditional expression
// (other than newly introduced locals), necessary for registering
// the associated triggers for when their values change.
const auto& WhenExprLocals() const { return when_expr_locals; }
const auto& WhenExprGlobals() const { return when_expr_globals; }
// The locals introduced in the conditional expression.
const auto& WhenNewLocals() const { return when_new_locals; }
// Used for script optimization when in-lining needs to revise
// identifiers.
bool HasUnreducedIDs(Reducer* c) const;
void UpdateIDs(Reducer* c);
private:
// Profile the original AST elements to extract things like
// globals and locals used.
void BuildProfile();
// Build those elements we'll need for invoking our lambda.
void BuildInvokeElems();
ExprPtr cond;
StmtPtr s;
StmtPtr timeout_s;
ExprPtr timeout;
FuncType::CaptureList* cl = nullptr;
bool is_return = false;
// The name of parameter passed to the lambda, and the corresponding
// identifier.
std::string lambda_param_id;
IDPtr param_id;
// The expression for constructing the lambda, and its type.
LambdaExprPtr lambda;
FuncTypePtr lambda_ft;
// The current instance of the lambda. Created by Instantiate(),
// for immediate use via calls to Cond() etc.
ConstExprPtr curr_lambda;
// Arguments to use when calling the lambda to either evaluate
// the conditional, or execute the body or the timeout statement.
ListExprPtr invoke_cond;
ListExprPtr invoke_s;
ListExprPtr invoke_timeout;
// Helper expressions for calling the lambda / testing within it.
ConstExprPtr one_const;
ConstExprPtr two_const;
ConstExprPtr three_const;
std::vector<IDPtr> when_expr_locals;
IDSet when_expr_globals;
// Locals introduced via "local" in the "when" clause itself.
IDSet when_new_locals;
};
class WhenStmt final : public Stmt {
public:
WhenStmt(std::shared_ptr<WhenInfo> wi);
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
bool IsPure() const override;
ExprPtr Cond() const { return wi->Cond(); }
StmtPtr Body() const { return wi->WhenBody(); }
ExprPtr TimeoutExpr() const { return wi->TimeoutExpr(); }
StmtPtr TimeoutBody() const { return wi->TimeoutStmt(); }
bool IsReturn() const { return wi->IsReturn(); }
auto Info() const { return wi; }
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
// Optimization-related:
StmtPtr Duplicate() override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
private:
std::shared_ptr<WhenInfo> wi;
};
// Internal statement used for inlining. Executes a block and stops
// the propagation of any "return" inside the block. Generated in
// an already-reduced state.
class CatchReturnStmt : public Stmt {
public:
explicit CatchReturnStmt(StmtPtr block, NameExprPtr ret_var);
StmtPtr Block() const { return block; }
// This returns a bare pointer rather than a NameExprPtr only
// because we don't want to have to include Expr.h in this header.
const NameExpr* RetVar() const { return ret_var.get(); }
// The assignment statement this statement transformed into,
// or nil if it hasn't (the common case).
StmtPtr AssignStmt() const { return assign_stmt; }
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
bool IsPure() const override;
// Even though these objects are generated in reduced form, we still
// have a reduction method to support the subsequent optimizer pass.
StmtPtr DoReduce(Reducer* c) override;
// Note, no need for a NoFlowAfter() method because anything that
// has "NoFlowAfter" inside the body still gets caught and we
// continue afterwards. Same goes for CouldReturn().
StmtPtr Duplicate() override;
void StmtDescribe(ODesc* d) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
protected:
// The inlined function body.
StmtPtr block;
// Expression that holds the return value. Only used for compiling.
NameExprPtr ret_var;
// If this statement transformed into an assignment, that
// corresponding statement.
StmtPtr assign_stmt;
};
// Statement that makes sure at run-time that an "any" type has the
// correct number of (list) entries to enable sub-assigning to it via
// statements like "[a, b, c] = x;". Generated in an already-reduced state.
class CheckAnyLenStmt : public ExprStmt {
public:
explicit CheckAnyLenStmt(ExprPtr e, int expected_len);
int ExpectedLen() const { return expected_len; }
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
StmtPtr Duplicate() override;
bool IsReduced(Reducer* c) const override;
StmtPtr DoReduce(Reducer* c) override;
void StmtDescribe(ODesc* d) const override;
protected:
int expected_len;
};
} // namespace zeek::detail