zeek/src/Stmt.h
Arne Welzel 171846a37a parse.y/directives: Reject directives as statements
Avoid the issue outlined in #2289 where the @if or @else is taken as the
statement of an `if`, `for` or `while` by rejecting such constructs.

Effectively this means the following scripts are now rejected:

    # Print's "cond true" with Zeek 5.0 even though the `if ( F )`
    # should be in effect.

    if ( F )
        @if ( T )
            print "cond true";
        @else
            print "cond false";
        @endif

or

    # Print's "hello" once with Zeek 5.0
    local v = vector( 1, 2, 3 );

    for ( i in v )
        @if ( T )
        print("hello")
        @endif

To make above work as intended, additional braces can be used.

    if ( T )
        {
    @if ( cond )
            print "cond true";
    @else
            print "cond false";
    @endif
        }

    for ( i in v )
        {
    @if ( T )
        print("hello")
    @endif
        }
2022-08-26 09:42:50 +02:00

761 lines
20 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;
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;
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".
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".
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; }
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:
StmtPtr DoReduce(Reducer* c) override;
bool NoFlowAfter(bool ignore_break) const override { return true; }
};
class StmtList : public Stmt
{
public:
StmtList();
~StmtList() override;
ValPtr Exec(Frame* f, StmtFlowType& flow) override;
const StmtPList& Stmts() const { return *stmts; }
StmtPList& 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;
// Idioms commonly used in reduction.
StmtList(StmtPtr s1, Stmt* s2);
StmtList(StmtPtr s1, StmtPtr s2);
StmtList(StmtPtr s1, StmtPtr s2, StmtPtr s3);
protected:
bool IsPure() const override;
StmtPList* stmts;
// Optimization-related:
bool ReduceStmt(int& s_i, StmtPList* f_stmts, Reducer* c);
void ResetStmts(StmtPList* new_stmts)
{
delete stmts;
stmts = 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;
};
// 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, which if nil signifies
// old-style frame semantics.
WhenInfo(ExprPtr cond, FuncType::CaptureList* cl, bool is_return);
// 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);
// For old-style semantics, the following simply return the
// individual "when" components. For capture semantics, however,
// these instead return different invocations of a lambda that
// manages the captures.
ExprPtr Cond();
StmtPtr WhenBody();
ExprPtr TimeoutExpr() const { return timeout; }
double TimeoutVal(Frame* f);
StmtPtr TimeoutStmt();
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 IDSet& WhenExprLocals() const { return when_expr_locals; }
const IDSet& WhenExprGlobals() const { return when_expr_globals; }
private:
// True if the "when" statement corresponds to old-style deprecated
// semantics (no captures, but needing captures). Also generates
// the corresponding deprecation warnings, which are associated
// with "ws".
bool IsDeprecatedSemantics(StmtPtr ws);
// Build those elements we'll need for invoking our lambda.
void BuildInvokeElems();
ExprPtr cond;
StmtPtr s;
ExprPtr timeout;
StmtPtr timeout_s;
FuncType::CaptureList* cl;
bool is_return = false;
// The name of parameter passed ot the lambda.
std::string lambda_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;
IDSet when_expr_locals;
IDSet when_expr_globals;
// Locals introduced via "local" in the "when" clause itself.
IDSet when_new_locals;
// Used for identifying deprecated instances. Holds all of the local
// variables in the scope prior to parsing the "when" statement.
std::map<std::string, IDPtr, std::less<>> prior_vars;
};
class WhenStmt final : public Stmt
{
public:
// The constructor takes ownership of the WhenInfo object.
WhenStmt(WhenInfo* wi);
~WhenStmt() override;
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(); }
const WhenInfo* Info() const { return wi; }
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;
private:
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.
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