"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()
{
delete field_name;
delete[] field_name;
}
ValPtr HasFieldExpr::Fold(Val* v) const

View file

@ -6,6 +6,7 @@
#include <memory>
#include "zeek/ActivationManager.h"
#include "zeek/Desc.h"
#include "zeek/EventRegistry.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()) )
add_prototype(id, t.get(), attr.get(), init);
else
else if ( activation_mgr->IsActivated() )
id->Error("already defined", init.get());
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,
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,
@ -711,6 +723,8 @@ void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool i
else if ( is_redef )
id->Error("redef of not-previously-declared value");
bool is_activated = activation_mgr->IsActivated();
if ( id->HasVal() )
{
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_HOOK:
if ( is_redef )
// Clear out value so it will be replaced.
id->SetVal(nullptr);
{
activation_mgr->RedefingHandler(id);
if ( ! is_activated )
// Clear out value so it will be replaced.
id->SetVal(nullptr);
}
break;
case FUNC_FLAVOR_FUNCTION:
if ( ! id->IsRedefinable() )
if ( ! id->IsRedefinable() && is_activated )
id->Error("already defined", t.get());
break;
@ -753,12 +772,15 @@ void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool i
if ( ! check_params(i, prototype, args, canon_args, module_name) )
break;
if ( Attr* depr_attr = find_attr(current_scope()->Attrs().get(), ATTR_DEPRECATED) )
current_scope()->GetID()->MakeDeprecated(depr_attr->GetExpr());
if ( is_activated )
{
if ( Attr* depr_attr = find_attr(current_scope()->Attrs().get(), ATTR_DEPRECATED) )
current_scope()->GetID()->MakeDeprecated(depr_attr->GetExpr());
// Reset the AST node statistics to track afresh for this function.
Stmt::ResetNumStmts();
Expr::ResetNumExprs();
// Reset the AST node statistics to track afresh for this function.
Stmt::ResetNumStmts();
Expr::ResetNumExprs();
}
}
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_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);
auto id = ingredients->GetID();
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);
id->SetVal(make_intrusive<FuncVal>(std::move(f)));
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());
auto func_ptr = cast_intrusive<FuncVal>(id->GetVal())->AsFuncPtr();
auto func = cast_intrusive<ScriptFunc>(func_ptr);
func->SetScope(ingredients->Scope());
for ( const auto& group : ingredients->Groups() )
group->AddFunc(func);
activation_mgr->AddingBody(id, ingredients);
analyze_func(std::move(func));
if ( activation_mgr->IsActivated() )
{
func->AddBody(ingredients->Body(), ingredients->Inits(), ingredients->FrameSize(),
ingredients->Priority(), ingredients->Groups());
// 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();
for ( const auto& group : ingredients->Groups() )
group->AddFunc(func);
analyze_func(std::move(func));
}
}
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 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_atifndef(const char* id);
extern void do_atelse();

View file

@ -5,10 +5,10 @@
// Switching parser table type fixes ambiguity problems.
%define lr.type ielr
%expect 211
%expect 212
%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_CONSTANT TOK_COPY TOK_COUNT TOK_DEFAULT TOK_DELETE
%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_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_NO_TEST
@ -98,6 +110,7 @@
#include "zeek/RE.h"
#include "zeek/Scope.h"
#include "zeek/Reporter.h"
#include "zeek/ActivationManager.h"
#include "zeek/ScriptCoverageManager.h"
#include "zeek/ScriptValidation.h"
#include "zeek/zeekygen/Manager.h"
@ -139,16 +152,19 @@ extern const char* g_curr_debug_error;
extern int in_when_cond;
static int in_hook = 0;
int in_init = 0;
int in_record = 0;
static int in_init = 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_enum_redef = 0;
bool resolving_global_ID = false;
bool defining_global_ID = false;
std::vector<int> saved_in_init;
static bool resolving_global_ID = false;
static bool defining_global_ID = false;
static bool is_activated = true;
static std::vector<int> saved_in_init;
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 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);
if ( ! activation_mgr->IsActivated() )
return;
if ( dt == VAR_REDEF )
zeekygen_mgr->Redef(id, ::filename, ic, std::move(e_ptr));
else
@ -392,9 +411,13 @@ zeek:
auto loc = zeek::detail::GetCurrentLocation();
if ( loc.filename )
set_location(loc);
++in_global_stmts;
}
stmt_list
{
--in_global_stmts;
if ( stmts )
stmts->AsStmtList()->Stmts().push_back($3);
else
@ -1400,29 +1423,49 @@ decl:
}
| 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 '}' ';'
{
if ( activation_mgr->InsideConditional() )
reporter->Error("enum redef cannot appear inside @if &analyze");
--in_enum_redef;
// Zeekygen already grabbed new enum IDs as the type created them.
}
| 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 '}' ';'
{
if ( activation_mgr->InsideConditional() )
reporter->Error("record redef cannot appear inside @if &analyze");
cur_decl_type_id = 0;
parse_redef_record_field($3, $5, INIT_EXTRA, std::unique_ptr<std::vector<AttrPtr>>($9));
}
| 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 '}' ';'
{
if ( activation_mgr->InsideConditional() )
reporter->Error("record redef cannot appear inside @if &analyze");
cur_decl_type_id = 0;
parse_redef_record_field($3, $5, INIT_REMOVE, std::unique_ptr<std::vector<AttrPtr>>($9));
}
| 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 '{'
{ ++in_record; ++in_record_redef; }
type_decl_list
@ -1433,6 +1476,8 @@ decl:
if ( ! $3->GetType() )
$3->Error("unknown identifier");
else if ( activation_mgr->InsideConditional() )
reporter->Error("record redef cannot appear inside @if &analyze");
else
extend_record($3, std::unique_ptr<type_decl_list>($8),
std::unique_ptr<std::vector<AttrPtr>>($11));
@ -1465,7 +1510,19 @@ conditional_list:
conditional:
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 ')'
{ do_atifdef($3); }
| TOK_ATIFNDEF '(' TOK_ID ')'
@ -1516,6 +1573,7 @@ func_body:
{
saved_in_init.push_back(in_init);
in_init = 0;
++in_body;
locals_at_this_scope.clear();
out_of_scope_locals.clear();
@ -1525,6 +1583,7 @@ func_body:
{
in_init = saved_in_init.back();
saved_in_init.pop_back();
--in_body;
}
'}'
@ -1545,12 +1604,14 @@ lambda_body:
{
saved_in_init.push_back(in_init);
in_init = 0;
++in_body;
}
stmt_list
{
in_init = saved_in_init.back();
saved_in_init.pop_back();
--in_body;
}
'}'
@ -1963,11 +2024,21 @@ stmt:
;
stmt_list:
stmt_list stmt
stmt_list { is_activated = activation_mgr->IsActivated(); } stmt
{
set_location(@1, @2);
$1->AsStmtList()->Stmts().push_back($2);
$1->UpdateLocationEndInfo(@2);
set_location(@1, @3);
// 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(); }
@ -2207,8 +2278,10 @@ global_or_event_id:
resolving_global_ID ?
current_module.c_str() : 0;
$$ = install_ID($1, module_name,
true, is_export).release();
auto gid = install_ID($1, module_name,
true, is_export);
activation_mgr->CreatingGlobalID(gid);
$$ = gid.release();
}
}
;

View file

@ -29,8 +29,6 @@
#include "zeek/DNS_Mgr.h"
#include "zeek/Expr.h"
#include "zeek/Func.h"
#include "zeek/Stmt.h"
#include "zeek/IntrusivePtr.h"
#include "zeek/Val.h"
#include "zeek/Var.h"
#include "zeek/Debug.h"
@ -41,6 +39,7 @@
#include "zeek/Traverse.h"
#include "zeek/module_util.h"
#include "zeek/ScannedFile.h"
#include "zeek/ActivationManager.h"
#include "zeek/analyzer/Analyzer.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;
// 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_act_depth; // @if &analyze 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
@ -127,6 +127,9 @@ static std::string find_relative_script_file(const std::string& filename)
static void start_conditional()
{
if ( activation_mgr->InsideConditional() )
zeek::reporter->Warning("@conditional inside @if &analyze");
++conditional_depth;
++conditional_epoch;
@ -155,17 +158,18 @@ static void do_pragma(const std::string& pragma)
if ( parts[0] == "push" )
{
if ( parts.size() < 2 )
{
zeek::reporter->FatalError("@pragma push without value");
return;
}
if ( parts.size() < 2 )
{
zeek::reporter->FatalError("@pragma push without value");
return;
}
if ( known_stack_pragmas.count(parts[1]) == 0 )
zeek::reporter->Warning("pushing unknown @pragma value '%s'", parts[1].c_str());
if ( known_stack_pragmas.count(parts[1]) == 0 )
zeek::reporter->Warning("pushing unknown @pragma value '%s'", parts[1].c_str());
pragma_stack.push_back(parts[1]);
pragma_stack.push_back(parts[1]);
}
else if ( parts[0] == "pop" )
{
if ( pragma_stack.empty() || pragma_stack.size() == entry_pragma_stack_depth.back() )
@ -176,10 +180,11 @@ static void do_pragma(const std::string& pragma)
// 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.
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'",
parts[1].c_str(), pragma_stack.back().c_str());
}
}
// Just pop anything
pragma_stack.pop_back();
@ -397,6 +402,8 @@ when return TOK_WHEN;
&backend return TOK_ATTR_BACKEND;
&ordered return TOK_ATTR_ORDERED;
")"{OWS}&analyze return TOK_ATTR_ANALYZE; // see parse.y for discussion of weirdness
@deprecated.* {
auto num_files = file_stack.length();
auto comment = zeek::util::skip_whitespace(yytext + 11);
@ -534,11 +541,12 @@ when return TOK_WHEN;
@endif do_atendif();
<IGNORE>@if start_conditional();
<IGNORE>&analyze zeek::reporter->Warning("@if &analyze inside regular @if");
<IGNORE>@ifdef start_conditional();
<IGNORE>@ifndef start_conditional();
<IGNORE>@else return TOK_ATELSE;
<IGNORE>@endif return TOK_ATENDIF;
<IGNORE>[^@\r\n]+ /* eat */
<IGNORE>[^@&\r\n]+ /* eat */
<IGNORE>. /* eat */
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;
entry_cond_depth.push_back(conditional_depth);
entry_act_depth.push_back(activation_mgr->ActivationDepth());
entry_pragma_stack_depth.push_back(pragma_stack.size());
return 1;
@ -846,10 +855,8 @@ static void resume_processing()
BEGIN(INITIAL);
}
void do_atif(zeek::detail::Expr* expr)
void do_atif(zeek::detail::Expr* expr, bool is_activate)
{
start_conditional();
LocalNameFinder cb;
expr->Traverse(&cb);
zeek::ValPtr val;
@ -865,11 +872,28 @@ void do_atif(zeek::detail::Expr* expr)
if ( ! val )
{
expr->Error("invalid expression in @if");
if ( ! is_activate )
start_conditional();
return;
}
if ( ! val->AsBool() )
begin_ignoring();
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();
}
}
void do_atifdef(const char* id)
@ -904,6 +928,12 @@ void do_atifndef(const char *id)
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 )
zeek::reporter->Error("@else without @if...");
@ -918,6 +948,15 @@ void do_atelse()
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() )
zeek::reporter->Error("unbalanced @if... @endif");
@ -1000,6 +1039,13 @@ int yywrap()
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 ( pragma_stack.size() > entry_pragma_stack_depth.back() )

View file

@ -23,6 +23,7 @@
#define DOCTEST_CONFIG_IMPLEMENT
#include "zeek/3rdparty/doctest.h"
#include "zeek/ActivationManager.h"
#include "zeek/Anon.h"
#include "zeek/DFA.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::DNS_Mgr* zeek::detail::dns_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::threading::Manager* zeek::thread_mgr = nullptr;
@ -443,6 +445,7 @@ static void terminate_zeek()
delete session_mgr;
delete fragment_mgr;
delete telemetry_mgr;
delete activation_mgr;
#ifdef HAVE_SPICY
delete spicy_mgr;
#endif
@ -673,6 +676,7 @@ SetupResult setup(int argc, char** argv, Options* zopts)
auto zeekygen_cfg = options.zeekygen_config_file.value_or("");
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("builtin-plugins/__preload__.zeek");