"if ( ... ) &analyze" language feature

This commit is contained in:
Vern Paxson 2023-05-19 12:46:01 -07:00
parent 052cda9df0
commit 9f4da24644
6 changed files with 207 additions and 62 deletions

View file

@ -3187,7 +3187,7 @@ HasFieldExpr::HasFieldExpr(ExprPtr arg_op, const char* arg_field_name)
HasFieldExpr::~HasFieldExpr() HasFieldExpr::~HasFieldExpr()
{ {
delete field_name; delete[] field_name;
} }
ValPtr HasFieldExpr::Fold(Val* v) const ValPtr HasFieldExpr::Fold(Val* v) const

View file

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include "zeek/ActivationManager.h"
#include "zeek/Desc.h" #include "zeek/Desc.h"
#include "zeek/EventRegistry.h" #include "zeek/EventRegistry.h"
#include "zeek/Expr.h" #include "zeek/Expr.h"
@ -234,7 +235,7 @@ static void make_var(const IDPtr& id, TypePtr t, InitClass c, ExprPtr init,
{ {
if ( IsFunc(id->GetType()->Tag()) ) if ( IsFunc(id->GetType()->Tag()) )
add_prototype(id, t.get(), attr.get(), init); add_prototype(id, t.get(), attr.get(), init);
else else if ( activation_mgr->IsActivated() )
id->Error("already defined", init.get()); id->Error("already defined", init.get());
return; return;
@ -405,7 +406,18 @@ static void make_var(const IDPtr& id, TypePtr t, InitClass c, ExprPtr init,
void add_global(const IDPtr& id, TypePtr t, InitClass c, ExprPtr init, void add_global(const IDPtr& id, TypePtr t, InitClass c, ExprPtr init,
std::unique_ptr<std::vector<AttrPtr>> attr, DeclType dt) std::unique_ptr<std::vector<AttrPtr>> attr, DeclType dt)
{ {
make_var(id, std::move(t), c, std::move(init), std::move(attr), dt, true); bool do_init = activation_mgr->IsActivated();
if ( dt == VAR_REDEF )
{
activation_mgr->AddingRedef(id, c, init, attr);
if ( ! do_init )
// Don't actually change the attributes.
attr = nullptr;
}
make_var(id, std::move(t), c, std::move(init), std::move(attr), dt, do_init);
} }
StmtPtr add_local(IDPtr id, TypePtr t, InitClass c, ExprPtr init, StmtPtr add_local(IDPtr id, TypePtr t, InitClass c, ExprPtr init,
@ -711,6 +723,8 @@ void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool i
else if ( is_redef ) else if ( is_redef )
id->Error("redef of not-previously-declared value"); id->Error("redef of not-previously-declared value");
bool is_activated = activation_mgr->IsActivated();
if ( id->HasVal() ) if ( id->HasVal() )
{ {
FunctionFlavor id_flavor = id->GetVal()->AsFunc()->Flavor(); FunctionFlavor id_flavor = id->GetVal()->AsFunc()->Flavor();
@ -724,12 +738,17 @@ void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool i
case FUNC_FLAVOR_EVENT: case FUNC_FLAVOR_EVENT:
case FUNC_FLAVOR_HOOK: case FUNC_FLAVOR_HOOK:
if ( is_redef ) if ( is_redef )
{
activation_mgr->RedefingHandler(id);
if ( ! is_activated )
// Clear out value so it will be replaced. // Clear out value so it will be replaced.
id->SetVal(nullptr); id->SetVal(nullptr);
}
break; break;
case FUNC_FLAVOR_FUNCTION: case FUNC_FLAVOR_FUNCTION:
if ( ! id->IsRedefinable() ) if ( ! id->IsRedefinable() && is_activated )
id->Error("already defined", t.get()); id->Error("already defined", t.get());
break; break;
@ -753,6 +772,8 @@ void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool i
if ( ! check_params(i, prototype, args, canon_args, module_name) ) if ( ! check_params(i, prototype, args, canon_args, module_name) )
break; break;
if ( is_activated )
{
if ( Attr* depr_attr = find_attr(current_scope()->Attrs().get(), ATTR_DEPRECATED) ) if ( Attr* depr_attr = find_attr(current_scope()->Attrs().get(), ATTR_DEPRECATED) )
current_scope()->GetID()->MakeDeprecated(depr_attr->GetExpr()); current_scope()->GetID()->MakeDeprecated(depr_attr->GetExpr());
@ -760,6 +781,7 @@ void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool i
Stmt::ResetNumStmts(); Stmt::ResetNumStmts();
Expr::ResetNumExprs(); Expr::ResetNumExprs();
} }
}
class OuterIDBindingFinder : public TraversalCallback class OuterIDBindingFinder : public TraversalCallback
{ {
@ -846,7 +868,7 @@ void end_func(StmtPtr body, const char* module_name, bool free_of_conditionals)
oi->num_stmts = Stmt::GetNumStmts(); oi->num_stmts = Stmt::GetNumStmts();
oi->num_exprs = Expr::GetNumExprs(); oi->num_exprs = Expr::GetNumExprs();
auto ingredients = std::make_unique<FunctionIngredients>(pop_scope(), std::move(body), auto ingredients = std::make_shared<FunctionIngredients>(pop_scope(), std::move(body),
module_name); module_name);
auto id = ingredients->GetID(); auto id = ingredients->GetID();
if ( ! id->HasVal() ) if ( ! id->HasVal() )
@ -854,27 +876,27 @@ void end_func(StmtPtr body, const char* module_name, bool free_of_conditionals)
auto f = make_intrusive<ScriptFunc>(id); auto f = make_intrusive<ScriptFunc>(id);
id->SetVal(make_intrusive<FuncVal>(std::move(f))); id->SetVal(make_intrusive<FuncVal>(std::move(f)));
id->SetConst(); id->SetConst();
activation_mgr->AddingGlobalVal(id);
} }
id->GetVal()->AsFunc()->AddBody(ingredients->Body(), ingredients->Inits(),
ingredients->FrameSize(), ingredients->Priority(),
ingredients->Groups());
script_coverage_mgr.AddFunction(id, ingredients->Body()); script_coverage_mgr.AddFunction(id, ingredients->Body());
auto func_ptr = cast_intrusive<FuncVal>(id->GetVal())->AsFuncPtr(); auto func_ptr = cast_intrusive<FuncVal>(id->GetVal())->AsFuncPtr();
auto func = cast_intrusive<ScriptFunc>(func_ptr); auto func = cast_intrusive<ScriptFunc>(func_ptr);
func->SetScope(ingredients->Scope()); func->SetScope(ingredients->Scope());
activation_mgr->AddingBody(id, ingredients);
if ( activation_mgr->IsActivated() )
{
func->AddBody(ingredients->Body(), ingredients->Inits(), ingredients->FrameSize(),
ingredients->Priority(), ingredients->Groups());
for ( const auto& group : ingredients->Groups() ) for ( const auto& group : ingredients->Groups() )
group->AddFunc(func); group->AddFunc(func);
analyze_func(std::move(func)); analyze_func(std::move(func));
}
// Note: ideally, something would take ownership of this memory until the
// end of script execution, but that's essentially the same as the
// lifetime of the process at the moment, so ok to "leak" it.
ingredients.release();
} }
IDPList gather_outer_ids(ScopePtr scope, StmtPtr body) IDPList gather_outer_ids(ScopePtr scope, StmtPtr body)

View file

@ -23,7 +23,7 @@ extern void add_to_name_list(char* s, char delim, zeek::name_list& nl);
extern void begin_RE(); extern void begin_RE();
extern void do_atif(zeek::detail::Expr* expr); extern void do_atif(zeek::detail::Expr* expr, bool is_activate);
extern void do_atifdef(const char* id); extern void do_atifdef(const char* id);
extern void do_atifndef(const char* id); extern void do_atifndef(const char* id);
extern void do_atelse(); extern void do_atelse();

View file

@ -5,10 +5,10 @@
// Switching parser table type fixes ambiguity problems. // Switching parser table type fixes ambiguity problems.
%define lr.type ielr %define lr.type ielr
%expect 211 %expect 212
%token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY %token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY
%token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF %token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATACTIVATEIF TOK_ATIFDEF TOK_ATIFNDEF
%token TOK_BOOL TOK_BREAK TOK_CASE TOK_OPTION TOK_CONST %token TOK_BOOL TOK_BREAK TOK_CASE TOK_OPTION TOK_CONST
%token TOK_CONSTANT TOK_COPY TOK_COUNT TOK_DEFAULT TOK_DELETE %token TOK_CONSTANT TOK_COPY TOK_COUNT TOK_DEFAULT TOK_DELETE
%token TOK_DOUBLE TOK_ELSE TOK_ENUM TOK_EVENT TOK_EXPORT TOK_FALLTHROUGH %token TOK_DOUBLE TOK_ELSE TOK_ENUM TOK_EVENT TOK_EXPORT TOK_FALLTHROUGH
@ -31,6 +31,18 @@
%token TOK_ATTR_TYPE_COLUMN TOK_ATTR_DEPRECATED %token TOK_ATTR_TYPE_COLUMN TOK_ATTR_DEPRECATED
%token TOK_ATTR_IS_ASSIGNED TOK_ATTR_IS_USED TOK_ATTR_ORDERED %token TOK_ATTR_IS_ASSIGNED TOK_ATTR_IS_USED TOK_ATTR_ORDERED
// Heads-up, this one is a weirdo. It combines both the attribute and
// a leading ')' before it (the two can be separated by spaces/tabs, but
// no newlines). This is necessary because if we use the more natural
//
// TOK_ATIF '(' expr ')' TOK_ATTR_ANALYZE
//
// then the parser needs to look ahead past the ')' to see if the attribute
// is there. If it *isn't*, then the scanner will return the first token
// of the conditional block for the look-ahead ... which will break the parse
// if that block should in fact have been skipped.
%token TOK_ATTR_ANALYZE
%token TOK_DEBUG %token TOK_DEBUG
%token TOK_NO_TEST %token TOK_NO_TEST
@ -98,6 +110,7 @@
#include "zeek/RE.h" #include "zeek/RE.h"
#include "zeek/Scope.h" #include "zeek/Scope.h"
#include "zeek/Reporter.h" #include "zeek/Reporter.h"
#include "zeek/ActivationManager.h"
#include "zeek/ScriptCoverageManager.h" #include "zeek/ScriptCoverageManager.h"
#include "zeek/ScriptValidation.h" #include "zeek/ScriptValidation.h"
#include "zeek/zeekygen/Manager.h" #include "zeek/zeekygen/Manager.h"
@ -139,16 +152,19 @@ extern const char* g_curr_debug_error;
extern int in_when_cond; extern int in_when_cond;
static int in_hook = 0; static int in_hook = 0;
int in_init = 0; static int in_init = 0;
int in_record = 0; static int in_body = 0;
static int in_global_stmts = 0;
static int in_record = 0;
static int in_record_redef = 0; static int in_record_redef = 0;
static int in_enum_redef = 0; static int in_enum_redef = 0;
bool resolving_global_ID = false; static bool resolving_global_ID = false;
bool defining_global_ID = false; static bool defining_global_ID = false;
std::vector<int> saved_in_init; static bool is_activated = true;
static std::vector<int> saved_in_init;
static int expr_list_has_opt_comma = 0; static int expr_list_has_opt_comma = 0;
std::vector<std::set<const ID*>> locals_at_this_scope; static std::vector<std::set<const ID*>> locals_at_this_scope;
static std::unordered_set<const ID*> out_of_scope_locals; static std::unordered_set<const ID*> out_of_scope_locals;
static Location func_hdr_location; static Location func_hdr_location;
@ -321,6 +337,9 @@ static void build_global(ID* id, Type* t, InitClass ic, Expr* e,
add_global(id_ptr, std::move(t_ptr), ic, e_ptr, std::move(attrs_ptr), dt); add_global(id_ptr, std::move(t_ptr), ic, e_ptr, std::move(attrs_ptr), dt);
if ( ! activation_mgr->IsActivated() )
return;
if ( dt == VAR_REDEF ) if ( dt == VAR_REDEF )
zeekygen_mgr->Redef(id, ::filename, ic, std::move(e_ptr)); zeekygen_mgr->Redef(id, ::filename, ic, std::move(e_ptr));
else else
@ -392,9 +411,13 @@ zeek:
auto loc = zeek::detail::GetCurrentLocation(); auto loc = zeek::detail::GetCurrentLocation();
if ( loc.filename ) if ( loc.filename )
set_location(loc); set_location(loc);
++in_global_stmts;
} }
stmt_list stmt_list
{ {
--in_global_stmts;
if ( stmts ) if ( stmts )
stmts->AsStmtList()->Stmts().push_back($3); stmts->AsStmtList()->Stmts().push_back($3);
else else
@ -1400,29 +1423,49 @@ decl:
} }
| TOK_REDEF TOK_ENUM global_id TOK_ADD_TO '{' | TOK_REDEF TOK_ENUM global_id TOK_ADD_TO '{'
{ ++in_enum_redef; parse_redef_enum($3); zeekygen_mgr->Redef($3, ::filename); } {
++in_enum_redef;
parse_redef_enum($3);
zeekygen_mgr->Redef($3, ::filename);
}
enum_body '}' ';' enum_body '}' ';'
{ {
if ( activation_mgr->InsideConditional() )
reporter->Error("enum redef cannot appear inside @if &analyze");
--in_enum_redef; --in_enum_redef;
// Zeekygen already grabbed new enum IDs as the type created them. // Zeekygen already grabbed new enum IDs as the type created them.
} }
| TOK_REDEF TOK_RECORD global_id '$' TOK_ID | TOK_REDEF TOK_RECORD global_id '$' TOK_ID
{ cur_decl_type_id = $3; zeekygen_mgr->Redef($3, ::filename, INIT_EXTRA); } {
cur_decl_type_id = $3;
zeekygen_mgr->Redef($3, ::filename, INIT_EXTRA);
}
TOK_ADD_TO '{' attr_list '}' ';' TOK_ADD_TO '{' attr_list '}' ';'
{ {
if ( activation_mgr->InsideConditional() )
reporter->Error("record redef cannot appear inside @if &analyze");
cur_decl_type_id = 0; cur_decl_type_id = 0;
parse_redef_record_field($3, $5, INIT_EXTRA, std::unique_ptr<std::vector<AttrPtr>>($9)); parse_redef_record_field($3, $5, INIT_EXTRA, std::unique_ptr<std::vector<AttrPtr>>($9));
} }
| TOK_REDEF TOK_RECORD global_id '$' TOK_ID | TOK_REDEF TOK_RECORD global_id '$' TOK_ID
{ cur_decl_type_id = $3; zeekygen_mgr->Redef($3, ::filename, INIT_REMOVE); } {
cur_decl_type_id = $3;
zeekygen_mgr->Redef($3, ::filename, INIT_REMOVE);
}
TOK_REMOVE_FROM '{' attr_list '}' ';' TOK_REMOVE_FROM '{' attr_list '}' ';'
{ {
if ( activation_mgr->InsideConditional() )
reporter->Error("record redef cannot appear inside @if &analyze");
cur_decl_type_id = 0; cur_decl_type_id = 0;
parse_redef_record_field($3, $5, INIT_REMOVE, std::unique_ptr<std::vector<AttrPtr>>($9)); parse_redef_record_field($3, $5, INIT_REMOVE, std::unique_ptr<std::vector<AttrPtr>>($9));
} }
| TOK_REDEF TOK_RECORD global_id | TOK_REDEF TOK_RECORD global_id
{ cur_decl_type_id = $3; zeekygen_mgr->Redef($3, ::filename); } {
cur_decl_type_id = $3;
zeekygen_mgr->Redef($3, ::filename);
}
TOK_ADD_TO '{' TOK_ADD_TO '{'
{ ++in_record; ++in_record_redef; } { ++in_record; ++in_record_redef; }
type_decl_list type_decl_list
@ -1433,6 +1476,8 @@ decl:
if ( ! $3->GetType() ) if ( ! $3->GetType() )
$3->Error("unknown identifier"); $3->Error("unknown identifier");
else if ( activation_mgr->InsideConditional() )
reporter->Error("record redef cannot appear inside @if &analyze");
else else
extend_record($3, std::unique_ptr<type_decl_list>($8), extend_record($3, std::unique_ptr<type_decl_list>($8),
std::unique_ptr<std::vector<AttrPtr>>($11)); std::unique_ptr<std::vector<AttrPtr>>($11));
@ -1465,7 +1510,19 @@ conditional_list:
conditional: conditional:
TOK_ATIF '(' expr ')' TOK_ATIF '(' expr ')'
{ do_atif($3); } { do_atif($3, false); }
| TOK_ATIF '(' expr TOK_ATTR_ANALYZE
{
if ( in_body )
reporter->Error("@if &analyze cannot appear inside a function body");
do_atif($3, true);
}
| TOK_ATACTIVATEIF '(' expr ')'
{
if ( in_body )
reporter->Error("@if &analyze cannot appear inside a function body");
do_atif($3, true);
}
| TOK_ATIFDEF '(' TOK_ID ')' | TOK_ATIFDEF '(' TOK_ID ')'
{ do_atifdef($3); } { do_atifdef($3); }
| TOK_ATIFNDEF '(' TOK_ID ')' | TOK_ATIFNDEF '(' TOK_ID ')'
@ -1516,6 +1573,7 @@ func_body:
{ {
saved_in_init.push_back(in_init); saved_in_init.push_back(in_init);
in_init = 0; in_init = 0;
++in_body;
locals_at_this_scope.clear(); locals_at_this_scope.clear();
out_of_scope_locals.clear(); out_of_scope_locals.clear();
@ -1525,6 +1583,7 @@ func_body:
{ {
in_init = saved_in_init.back(); in_init = saved_in_init.back();
saved_in_init.pop_back(); saved_in_init.pop_back();
--in_body;
} }
'}' '}'
@ -1545,12 +1604,14 @@ lambda_body:
{ {
saved_in_init.push_back(in_init); saved_in_init.push_back(in_init);
in_init = 0; in_init = 0;
++in_body;
} }
stmt_list stmt_list
{ {
in_init = saved_in_init.back(); in_init = saved_in_init.back();
saved_in_init.pop_back(); saved_in_init.pop_back();
--in_body;
} }
'}' '}'
@ -1963,11 +2024,21 @@ stmt:
; ;
stmt_list: stmt_list:
stmt_list stmt stmt_list { is_activated = activation_mgr->IsActivated(); } stmt
{ {
set_location(@1, @2); set_location(@1, @3);
$1->AsStmtList()->Stmts().push_back($2);
$1->UpdateLocationEndInfo(@2); // We can't simply test activation_mgr->IsActivated()
// here because the parser can wind up looking ahead
// to the @endif token and restoring activation that
// in fact was off for the statement. So we capture
// the activation state prior to parsing the statement
// in "is_activated" and test that instead.
if ( ! in_global_stmts || is_activated )
{
$1->AsStmtList()->Stmts().push_back($3);
$1->UpdateLocationEndInfo(@3);
}
} }
| |
{ $$ = new StmtList(); } { $$ = new StmtList(); }
@ -2207,8 +2278,10 @@ global_or_event_id:
resolving_global_ID ? resolving_global_ID ?
current_module.c_str() : 0; current_module.c_str() : 0;
$$ = install_ID($1, module_name, auto gid = install_ID($1, module_name,
true, is_export).release(); true, is_export);
activation_mgr->CreatingGlobalID(gid);
$$ = gid.release();
} }
} }
; ;

View file

@ -29,8 +29,6 @@
#include "zeek/DNS_Mgr.h" #include "zeek/DNS_Mgr.h"
#include "zeek/Expr.h" #include "zeek/Expr.h"
#include "zeek/Func.h" #include "zeek/Func.h"
#include "zeek/Stmt.h"
#include "zeek/IntrusivePtr.h"
#include "zeek/Val.h" #include "zeek/Val.h"
#include "zeek/Var.h" #include "zeek/Var.h"
#include "zeek/Debug.h" #include "zeek/Debug.h"
@ -41,6 +39,7 @@
#include "zeek/Traverse.h" #include "zeek/Traverse.h"
#include "zeek/module_util.h" #include "zeek/module_util.h"
#include "zeek/ScannedFile.h" #include "zeek/ScannedFile.h"
#include "zeek/ActivationManager.h"
#include "zeek/analyzer/Analyzer.h" #include "zeek/analyzer/Analyzer.h"
#include "zeek/zeekygen/Manager.h" #include "zeek/zeekygen/Manager.h"
@ -55,9 +54,10 @@ extern YYLTYPE yylloc; // holds start line and column of token
extern zeek::EnumType* cur_enum_type; extern zeek::EnumType* cur_enum_type;
// Track the @if... depth. // Track the @if... depth.
static std::intptr_t conditional_depth = 0; static int conditional_depth = 0;
zeek::detail::int_list entry_cond_depth; // @if depth upon starting file zeek::detail::int_list entry_cond_depth; // @if depth upon starting file
zeek::detail::int_list entry_act_depth; // @if &analyze depth upon starting file
zeek::detail::int_list entry_pragma_stack_depth; // @pragma push depth upon starting file zeek::detail::int_list entry_pragma_stack_depth; // @pragma push depth upon starting file
static std::vector<std::string> pragma_stack; // stack of @pragma pushes static std::vector<std::string> pragma_stack; // stack of @pragma pushes
@ -127,6 +127,9 @@ static std::string find_relative_script_file(const std::string& filename)
static void start_conditional() static void start_conditional()
{ {
if ( activation_mgr->InsideConditional() )
zeek::reporter->Warning("@conditional inside @if &analyze");
++conditional_depth; ++conditional_depth;
++conditional_epoch; ++conditional_epoch;
@ -166,6 +169,7 @@ static void do_pragma(const std::string& pragma)
pragma_stack.push_back(parts[1]); pragma_stack.push_back(parts[1]);
} }
else if ( parts[0] == "pop" ) else if ( parts[0] == "pop" )
{ {
if ( pragma_stack.empty() || pragma_stack.size() == entry_pragma_stack_depth.back() ) if ( pragma_stack.empty() || pragma_stack.size() == entry_pragma_stack_depth.back() )
@ -176,7 +180,8 @@ static void do_pragma(const std::string& pragma)
// Popping with a value: Verify it's popping the right thing. Not providing // Popping with a value: Verify it's popping the right thing. Not providing
// a pop value itself is valid. Don't return, probably blows up below anyway. // a pop value itself is valid. Don't return, probably blows up below anyway.
if ( parts.size() > 1 && pragma_stack.back() != parts[1] ) { if ( parts.size() > 1 && pragma_stack.back() != parts[1] )
{
zeek::reporter->Error("@pragma pop with unexpected '%s', expected '%s'", zeek::reporter->Error("@pragma pop with unexpected '%s', expected '%s'",
parts[1].c_str(), pragma_stack.back().c_str()); parts[1].c_str(), pragma_stack.back().c_str());
} }
@ -397,6 +402,8 @@ when return TOK_WHEN;
&backend return TOK_ATTR_BACKEND; &backend return TOK_ATTR_BACKEND;
&ordered return TOK_ATTR_ORDERED; &ordered return TOK_ATTR_ORDERED;
")"{OWS}&analyze return TOK_ATTR_ANALYZE; // see parse.y for discussion of weirdness
@deprecated.* { @deprecated.* {
auto num_files = file_stack.length(); auto num_files = file_stack.length();
auto comment = zeek::util::skip_whitespace(yytext + 11); auto comment = zeek::util::skip_whitespace(yytext + 11);
@ -534,11 +541,12 @@ when return TOK_WHEN;
@endif do_atendif(); @endif do_atendif();
<IGNORE>@if start_conditional(); <IGNORE>@if start_conditional();
<IGNORE>&analyze zeek::reporter->Warning("@if &analyze inside regular @if");
<IGNORE>@ifdef start_conditional(); <IGNORE>@ifdef start_conditional();
<IGNORE>@ifndef start_conditional(); <IGNORE>@ifndef start_conditional();
<IGNORE>@else return TOK_ATELSE; <IGNORE>@else return TOK_ATELSE;
<IGNORE>@endif return TOK_ATENDIF; <IGNORE>@endif return TOK_ATENDIF;
<IGNORE>[^@\r\n]+ /* eat */ <IGNORE>[^@&\r\n]+ /* eat */
<IGNORE>. /* eat */ <IGNORE>. /* eat */
T RET_CONST(zeek::val_mgr->True()->Ref()) T RET_CONST(zeek::val_mgr->True()->Ref())
@ -802,6 +810,7 @@ static int load_files(const char* orig_file)
current_file_has_conditionals = files_with_conditionals.count(filename) > 0; current_file_has_conditionals = files_with_conditionals.count(filename) > 0;
entry_cond_depth.push_back(conditional_depth); entry_cond_depth.push_back(conditional_depth);
entry_act_depth.push_back(activation_mgr->ActivationDepth());
entry_pragma_stack_depth.push_back(pragma_stack.size()); entry_pragma_stack_depth.push_back(pragma_stack.size());
return 1; return 1;
@ -846,10 +855,8 @@ static void resume_processing()
BEGIN(INITIAL); BEGIN(INITIAL);
} }
void do_atif(zeek::detail::Expr* expr) void do_atif(zeek::detail::Expr* expr, bool is_activate)
{ {
start_conditional();
LocalNameFinder cb; LocalNameFinder cb;
expr->Traverse(&cb); expr->Traverse(&cb);
zeek::ValPtr val; zeek::ValPtr val;
@ -865,12 +872,29 @@ void do_atif(zeek::detail::Expr* expr)
if ( ! val ) if ( ! val )
{ {
expr->Error("invalid expression in @if"); expr->Error("invalid expression in @if");
if ( ! is_activate )
start_conditional();
return; return;
} }
if ( ! val->AsBool() ) bool is_true = val->AsBool();
if ( is_activate )
{
if ( conditional_depth > 0 )
zeek::reporter->Warning("@if &analyze inside conditional");
activation_mgr->Start({zeek::NewRef{}, expr}, is_true, conditional_depth);
}
else
{
start_conditional();
if ( ! is_true )
begin_ignoring(); begin_ignoring();
} }
}
void do_atifdef(const char* id) void do_atifdef(const char* id)
{ {
@ -904,6 +928,12 @@ void do_atifndef(const char *id)
void do_atelse() void do_atelse()
{ {
if ( activation_mgr->InsideConditional(conditional_depth) )
{ // This is the @else corresponding to an @if &analyze.
activation_mgr->SwitchToElse();
return;
}
if ( conditional_depth == 0 ) if ( conditional_depth == 0 )
zeek::reporter->Error("@else without @if..."); zeek::reporter->Error("@else without @if...");
@ -918,6 +948,15 @@ void do_atelse()
void do_atendif() void do_atendif()
{ {
if ( activation_mgr->InsideConditional(conditional_depth) )
{ // We're ending an @if &analyze.
if ( activation_mgr->ActivationDepth() <= entry_act_depth.back() )
zeek::reporter->Error("unbalanced @if &analyze... @endif");
else
activation_mgr->End();
return;
}
if ( conditional_depth <= entry_cond_depth.back() ) if ( conditional_depth <= entry_cond_depth.back() )
zeek::reporter->Error("unbalanced @if... @endif"); zeek::reporter->Error("unbalanced @if... @endif");
@ -1000,6 +1039,13 @@ int yywrap()
entry_cond_depth.pop_back(); entry_cond_depth.pop_back();
} }
if ( entry_act_depth.size() > 0 )
{
if ( activation_mgr->ActivationDepth() > entry_act_depth.back() )
zeek::reporter->FatalError("unbalanced @if &analyze... @endif");
entry_act_depth.pop_back();
}
if ( entry_pragma_stack_depth.size() > 0 ) if ( entry_pragma_stack_depth.size() > 0 )
{ {
if ( pragma_stack.size() > entry_pragma_stack_depth.back() ) if ( pragma_stack.size() > entry_pragma_stack_depth.back() )

View file

@ -23,6 +23,7 @@
#define DOCTEST_CONFIG_IMPLEMENT #define DOCTEST_CONFIG_IMPLEMENT
#include "zeek/3rdparty/doctest.h" #include "zeek/3rdparty/doctest.h"
#include "zeek/ActivationManager.h"
#include "zeek/Anon.h" #include "zeek/Anon.h"
#include "zeek/DFA.h" #include "zeek/DFA.h"
#include "zeek/DNS_Mgr.h" #include "zeek/DNS_Mgr.h"
@ -183,6 +184,7 @@ zeek::plugin::Manager* zeek::plugin_mgr = nullptr;
zeek::detail::RuleMatcher* zeek::detail::rule_matcher = nullptr; zeek::detail::RuleMatcher* zeek::detail::rule_matcher = nullptr;
zeek::detail::DNS_Mgr* zeek::detail::dns_mgr = nullptr; zeek::detail::DNS_Mgr* zeek::detail::dns_mgr = nullptr;
zeek::detail::TimerMgr* zeek::detail::timer_mgr = nullptr; zeek::detail::TimerMgr* zeek::detail::timer_mgr = nullptr;
zeek::detail::ActivationManager* zeek::detail::activation_mgr;
zeek::logging::Manager* zeek::log_mgr = nullptr; zeek::logging::Manager* zeek::log_mgr = nullptr;
zeek::threading::Manager* zeek::thread_mgr = nullptr; zeek::threading::Manager* zeek::thread_mgr = nullptr;
@ -443,6 +445,7 @@ static void terminate_zeek()
delete session_mgr; delete session_mgr;
delete fragment_mgr; delete fragment_mgr;
delete telemetry_mgr; delete telemetry_mgr;
delete activation_mgr;
#ifdef HAVE_SPICY #ifdef HAVE_SPICY
delete spicy_mgr; delete spicy_mgr;
#endif #endif
@ -673,6 +676,7 @@ SetupResult setup(int argc, char** argv, Options* zopts)
auto zeekygen_cfg = options.zeekygen_config_file.value_or(""); auto zeekygen_cfg = options.zeekygen_config_file.value_or("");
zeekygen_mgr = new zeekygen::detail::Manager(zeekygen_cfg, zeek_argv[0]); zeekygen_mgr = new zeekygen::detail::Manager(zeekygen_cfg, zeek_argv[0]);
activation_mgr = new ActivationManager();
add_essential_input_file("base/init-bare.zeek"); add_essential_input_file("base/init-bare.zeek");
add_essential_input_file("builtin-plugins/__preload__.zeek"); add_essential_input_file("builtin-plugins/__preload__.zeek");