mirror of
https://github.com/zeek/zeek.git
synced 2025-10-04 15:48:19 +00:00
Merge remote-tracking branch 'origin/topic/jsiwek/hook'
* origin/topic/jsiwek/hook: Add memory leak unit test for "hook" function flavor. Add new function flavor called a "hook".
This commit is contained in:
commit
d9bb9e0eb1
31 changed files with 578 additions and 79 deletions
|
@ -417,10 +417,6 @@ The Bro scripting language supports the following built-in types.
|
||||||
Writing to files like this for logging usually isn't recommended, for better
|
Writing to files like this for logging usually isn't recommended, for better
|
||||||
logging support see :doc:`/logging`.
|
logging support see :doc:`/logging`.
|
||||||
|
|
||||||
.. bro:type:: func
|
|
||||||
|
|
||||||
See :bro:type:`function`.
|
|
||||||
|
|
||||||
.. bro:type:: function
|
.. bro:type:: function
|
||||||
|
|
||||||
Function types in Bro are declared using::
|
Function types in Bro are declared using::
|
||||||
|
@ -504,6 +500,74 @@ The Bro scripting language supports the following built-in types.
|
||||||
identifier and the body of each will be executed in turn. Ordering
|
identifier and the body of each will be executed in turn. Ordering
|
||||||
of execution can be influenced with :bro:attr:`&priority`.
|
of execution can be influenced with :bro:attr:`&priority`.
|
||||||
|
|
||||||
|
.. bro:type:: hook
|
||||||
|
|
||||||
|
A hook is another flavor of function that shares characteristics of
|
||||||
|
both a :bro:type:`function` and a :bro:type:`event`. They are like
|
||||||
|
events in that many handler bodies can be defined for the same hook
|
||||||
|
identifier, they have no return vale, and the order of execution
|
||||||
|
can be enforced with :bro:attr:`&priority`. They are more like
|
||||||
|
functions in the way they are invoked/called, because, unlike
|
||||||
|
events, their execution is immediate and they do not get scheduled
|
||||||
|
through an event queue. Also, a unique feature of a hook is that
|
||||||
|
a given hook handler body can short-circuit the execution of
|
||||||
|
remaining hook handlers simply by exiting from the body as a result
|
||||||
|
of a ``break`` statement (as opposed to a ``return`` or just
|
||||||
|
reaching the end of the body).
|
||||||
|
|
||||||
|
A hook type is declared like::
|
||||||
|
|
||||||
|
hook( argument* )
|
||||||
|
|
||||||
|
where *argument* is a (possibly empty) comma-separated list of
|
||||||
|
arguments. For example:
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
global myhook: hook(s: string)
|
||||||
|
|
||||||
|
Here ``myhook`` is the hook type identifier and no hook handler
|
||||||
|
bodies have been defined for it yet. To define some hook handler
|
||||||
|
bodies the syntax looks like:
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
hook myhook(s: string) &priority=10
|
||||||
|
{
|
||||||
|
print "priority 10 myhook handler", s;
|
||||||
|
s = "bye";
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook(s: string)
|
||||||
|
{
|
||||||
|
print "break out of myhook handling", s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook(s: string) &priority=-5
|
||||||
|
{
|
||||||
|
print "not going to happen", s;
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that, although the first (forward) declaration of ``myhook`` as
|
||||||
|
a hook type isn't strictly required, when it is provided, the
|
||||||
|
argument types must match.
|
||||||
|
|
||||||
|
To invoke immediate execution of all hook handler bodies, a ``hook``
|
||||||
|
statement must be used:
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
hook myhook("hi");
|
||||||
|
|
||||||
|
And the output would like like::
|
||||||
|
|
||||||
|
priority 10 myhook handler, hi
|
||||||
|
break out of myhook handling, bye
|
||||||
|
|
||||||
|
Note how the modification to arguments can be seen by remaining
|
||||||
|
hook handlers.
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,9 @@ void Attr::DescribeReST(ODesc* d) const
|
||||||
|
|
||||||
else if ( expr->Type()->Tag() == TYPE_FUNC )
|
else if ( expr->Type()->Tag() == TYPE_FUNC )
|
||||||
{
|
{
|
||||||
d->Add(":bro:type:`func`");
|
d->Add(":bro:type:`");
|
||||||
|
d->Add(expr->Type()->AsFuncType()->FlavorString());
|
||||||
|
d->Add("`");
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -401,13 +403,13 @@ void Attributes::CheckAttr(Attr* a)
|
||||||
|
|
||||||
case ATTR_GROUP:
|
case ATTR_GROUP:
|
||||||
if ( type->Tag() != TYPE_FUNC ||
|
if ( type->Tag() != TYPE_FUNC ||
|
||||||
! type->AsFuncType()->IsEvent() )
|
type->AsFuncType()->Flavor() != FUNC_FLAVOR_EVENT )
|
||||||
Error("&group only applicable to events");
|
Error("&group only applicable to events");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ATTR_ERROR_HANDLER:
|
case ATTR_ERROR_HANDLER:
|
||||||
if ( type->Tag() != TYPE_FUNC ||
|
if ( type->Tag() != TYPE_FUNC ||
|
||||||
! type->AsFuncType()->IsEvent() )
|
type->AsFuncType()->Flavor() != FUNC_FLAVOR_EVENT )
|
||||||
Error("&error_handler only applicable to events");
|
Error("&error_handler only applicable to events");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -271,6 +271,7 @@ void BroDoc::WriteInterface(const char* heading, char underline,
|
||||||
WriteBroDocObjList(state_vars, isPublic, "State Variables", sub, isShort);
|
WriteBroDocObjList(state_vars, isPublic, "State Variables", sub, isShort);
|
||||||
WriteBroDocObjList(types, isPublic, "Types", sub, isShort);
|
WriteBroDocObjList(types, isPublic, "Types", sub, isShort);
|
||||||
WriteBroDocObjList(events, isPublic, "Events", sub, isShort);
|
WriteBroDocObjList(events, isPublic, "Events", sub, isShort);
|
||||||
|
WriteBroDocObjList(hooks, isPublic, "Hooks", sub, isShort);
|
||||||
WriteBroDocObjList(functions, isPublic, "Functions", sub, isShort);
|
WriteBroDocObjList(functions, isPublic, "Functions", sub, isShort);
|
||||||
WriteBroDocObjList(redefs, isPublic, "Redefinitions", sub, isShort);
|
WriteBroDocObjList(redefs, isPublic, "Redefinitions", sub, isShort);
|
||||||
}
|
}
|
||||||
|
|
26
src/BroDoc.h
26
src/BroDoc.h
|
@ -179,6 +179,30 @@ public:
|
||||||
all.push_back(o);
|
all.push_back(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules documentation of a hook declared by the script.
|
||||||
|
* @param o A pointer to a BroDocObj which contains the internal
|
||||||
|
* Bro language representation of the script hook and
|
||||||
|
* also any associated comments about it.
|
||||||
|
*/
|
||||||
|
void AddHook(const BroDocObj* o)
|
||||||
|
{
|
||||||
|
hooks.push_back(o);
|
||||||
|
all.push_back(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules documentation of a hook handler declared by the script.
|
||||||
|
* @param o A pointer to a BroDocObj which contains the internal
|
||||||
|
* Bro language representation of the script hook handler and
|
||||||
|
* also any associated comments about it.
|
||||||
|
*/
|
||||||
|
void AddHookHandler(const BroDocObj* o)
|
||||||
|
{
|
||||||
|
hook_handlers.push_back(o);
|
||||||
|
all.push_back(o);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules documentation of a function declared by the script.
|
* Schedules documentation of a function declared by the script.
|
||||||
* @param o A pointer to a BroDocObj which contains the internal
|
* @param o A pointer to a BroDocObj which contains the internal
|
||||||
|
@ -241,6 +265,8 @@ protected:
|
||||||
BroDocObjList notices;
|
BroDocObjList notices;
|
||||||
BroDocObjList events;
|
BroDocObjList events;
|
||||||
BroDocObjList event_handlers;
|
BroDocObjList event_handlers;
|
||||||
|
BroDocObjList hooks;
|
||||||
|
BroDocObjList hook_handlers;
|
||||||
BroDocObjMap functions;
|
BroDocObjMap functions;
|
||||||
BroDocObjList redefs;
|
BroDocObjList redefs;
|
||||||
|
|
||||||
|
|
29
src/Expr.cc
29
src/Expr.cc
|
@ -4374,7 +4374,7 @@ bool InExpr::DoUnserialize(UnserialInfo* info)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CallExpr::CallExpr(Expr* arg_func, ListExpr* arg_args)
|
CallExpr::CallExpr(Expr* arg_func, ListExpr* arg_args, bool in_hook)
|
||||||
: Expr(EXPR_CALL)
|
: Expr(EXPR_CALL)
|
||||||
{
|
{
|
||||||
func = arg_func;
|
func = arg_func;
|
||||||
|
@ -4402,8 +4402,33 @@ CallExpr::CallExpr(Expr* arg_func, ListExpr* arg_args)
|
||||||
|
|
||||||
if ( ! yield )
|
if ( ! yield )
|
||||||
{
|
{
|
||||||
Error("event called in expression");
|
switch ( func_type->AsFuncType()->Flavor() ) {
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_FUNCTION:
|
||||||
|
Error("function has no yield type");
|
||||||
SetError();
|
SetError();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_EVENT:
|
||||||
|
Error("event called in expression, use event statement instead");
|
||||||
|
SetError();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_HOOK:
|
||||||
|
// It's fine to not have a yield if it's known that the call
|
||||||
|
// is being done from a hook statement.
|
||||||
|
if ( ! in_hook )
|
||||||
|
{
|
||||||
|
Error("hook called in expression, use hook statement instead");
|
||||||
|
SetError();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Error("invalid function flavor");
|
||||||
|
SetError();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
SetType(yield->Ref());
|
SetType(yield->Ref());
|
||||||
|
|
|
@ -959,7 +959,7 @@ protected:
|
||||||
|
|
||||||
class CallExpr : public Expr {
|
class CallExpr : public Expr {
|
||||||
public:
|
public:
|
||||||
CallExpr(Expr* func, ListExpr* args);
|
CallExpr(Expr* func, ListExpr* args, bool in_hook = false);
|
||||||
~CallExpr();
|
~CallExpr();
|
||||||
|
|
||||||
Expr* Func() const { return func; }
|
Expr* Func() const { return func; }
|
||||||
|
|
14
src/Func.cc
14
src/Func.cc
|
@ -284,8 +284,8 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const
|
||||||
#endif
|
#endif
|
||||||
if ( ! bodies.size() )
|
if ( ! bodies.size() )
|
||||||
{
|
{
|
||||||
// Can only happen for events.
|
// Can only happen for events and hooks.
|
||||||
assert(IsEvent());
|
assert(Flavor() == FUNC_FLAVOR_EVENT || Flavor() == FUNC_FLAVOR_HOOK);
|
||||||
loop_over_list(*args, i)
|
loop_over_list(*args, i)
|
||||||
Unref((*args)[i]);
|
Unref((*args)[i]);
|
||||||
return 0 ;
|
return 0 ;
|
||||||
|
@ -309,7 +309,7 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const
|
||||||
DescribeDebug(&d, args);
|
DescribeDebug(&d, args);
|
||||||
|
|
||||||
g_trace_state.LogTrace("%s called: %s\n",
|
g_trace_state.LogTrace("%s called: %s\n",
|
||||||
IsEvent() ? "event" : "function", d.Description());
|
FType()->FlavorString().c_str(), d.Description());
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_over_list(*args, i)
|
loop_over_list(*args, i)
|
||||||
|
@ -348,6 +348,12 @@ Val* BroFunc::Call(val_list* args, Frame* parent) const
|
||||||
parent->SetDelayed();
|
parent->SetDelayed();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( flow == FLOW_BREAK && Flavor() == FUNC_FLAVOR_HOOK )
|
||||||
|
{
|
||||||
|
// short-circuit execution of remaining hook handler bodies
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn if the function returns something, but we returned from
|
// Warn if the function returns something, but we returned from
|
||||||
|
@ -380,7 +386,7 @@ void BroFunc::AddBody(Stmt* new_body, id_list* new_inits, int new_frame_size,
|
||||||
|
|
||||||
new_body = AddInits(new_body, new_inits);
|
new_body = AddInits(new_body, new_inits);
|
||||||
|
|
||||||
if ( ! IsEvent() )
|
if ( Flavor() == FUNC_FLAVOR_FUNCTION )
|
||||||
{
|
{
|
||||||
// For functions, we replace the old body with the new one.
|
// For functions, we replace the old body with the new one.
|
||||||
assert(bodies.size() <= 1);
|
assert(bodies.size() <= 1);
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
virtual ~Func();
|
virtual ~Func();
|
||||||
|
|
||||||
virtual int IsPure() const = 0;
|
virtual int IsPure() const = 0;
|
||||||
int IsEvent() const { return FType()->IsEvent(); }
|
function_flavor Flavor() const { return FType()->Flavor(); }
|
||||||
|
|
||||||
struct Body {
|
struct Body {
|
||||||
Stmt* stmts;
|
Stmt* stmts;
|
||||||
|
|
|
@ -107,7 +107,8 @@ void ID::SetVal(Val* v, Opcode op, bool arg_weak_ref)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( type && val &&
|
if ( type && val &&
|
||||||
type->Tag() == TYPE_FUNC && type->AsFuncType()->IsEvent() )
|
type->Tag() == TYPE_FUNC &&
|
||||||
|
type->AsFuncType()->Flavor() == FUNC_FLAVOR_EVENT )
|
||||||
{
|
{
|
||||||
EventHandler* handler = event_registry->Lookup(name);
|
EventHandler* handler = event_registry->Lookup(name);
|
||||||
if ( ! handler )
|
if ( ! handler )
|
||||||
|
@ -657,7 +658,7 @@ void ID::DescribeReSTShort(ODesc* d) const
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TYPE_FUNC:
|
case TYPE_FUNC:
|
||||||
d->Add(type->AsFuncType()->IsEvent() ? "event" : type_name(t));
|
d->Add(type->AsFuncType()->FlavorString());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TYPE_ENUM:
|
case TYPE_ENUM:
|
||||||
|
|
|
@ -165,6 +165,7 @@ SERIAL_STMT(EVENT_BODY_LIST, 16)
|
||||||
SERIAL_STMT(INIT_STMT, 17)
|
SERIAL_STMT(INIT_STMT, 17)
|
||||||
SERIAL_STMT(NULL_STMT, 18)
|
SERIAL_STMT(NULL_STMT, 18)
|
||||||
SERIAL_STMT(WHEN_STMT, 19)
|
SERIAL_STMT(WHEN_STMT, 19)
|
||||||
|
SERIAL_STMT(HOOK_STMT, 20)
|
||||||
|
|
||||||
#define SERIAL_TYPE(name, val) SERIAL_CONST(name, val, BRO_TYPE)
|
#define SERIAL_TYPE(name, val) SERIAL_CONST(name, val, BRO_TYPE)
|
||||||
SERIAL_TYPE(BRO_TYPE, 1)
|
SERIAL_TYPE(BRO_TYPE, 1)
|
||||||
|
|
|
@ -389,23 +389,35 @@ bool Serializer::UnserializeCall(UnserialInfo* info)
|
||||||
{
|
{
|
||||||
if ( info->print )
|
if ( info->print )
|
||||||
fprintf(info->print, "%s [%.06f] %s(%s)\n",
|
fprintf(info->print, "%s [%.06f] %s(%s)\n",
|
||||||
functype->IsEvent() ? "Event" : "Function call",
|
functype->FlavorString().c_str(),
|
||||||
time, name, types ? d.Description() : "<ignored>");
|
time, name, types ? d.Description() : "<ignored>");
|
||||||
|
|
||||||
if ( functype->IsEvent() )
|
switch ( functype->Flavor() ) {
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_EVENT:
|
||||||
{
|
{
|
||||||
EventHandler* handler = event_registry->Lookup(name);
|
EventHandler* handler = event_registry->Lookup(name);
|
||||||
assert(handler);
|
assert(handler);
|
||||||
|
|
||||||
if ( ! info->ignore_callbacks )
|
if ( ! info->ignore_callbacks )
|
||||||
GotEvent(name, time, handler, args);
|
GotEvent(name, time, handler, args);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
case FUNC_FLAVOR_FUNCTION:
|
||||||
|
case FUNC_FLAVOR_HOOK:
|
||||||
if ( ! info->ignore_callbacks )
|
if ( ! info->ignore_callbacks )
|
||||||
GotFunctionCall(name, time, id->ID_Val()->AsFunc(), args);
|
GotFunctionCall(name, time, id->ID_Val()->AsFunc(), args);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
reporter->InternalError("invalid function flavor");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if ( info->ignore_callbacks )
|
if ( info->ignore_callbacks )
|
||||||
delete_vals(args);
|
delete_vals(args);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
delete_vals(args);
|
delete_vals(args);
|
||||||
|
|
|
@ -125,7 +125,7 @@ protected:
|
||||||
|
|
||||||
// This will be increased whenever there is an incompatible change
|
// This will be increased whenever there is an incompatible change
|
||||||
// in the data format.
|
// in the data format.
|
||||||
static const uint32 DATA_FORMAT_VERSION = 22;
|
static const uint32 DATA_FORMAT_VERSION = 23;
|
||||||
|
|
||||||
ChunkedIO* io;
|
ChunkedIO* io;
|
||||||
|
|
||||||
|
|
49
src/Stmt.cc
49
src/Stmt.cc
|
@ -23,7 +23,7 @@ const char* stmt_name(BroStmtTag t)
|
||||||
"print", "event", "expr", "if", "when", "switch",
|
"print", "event", "expr", "if", "when", "switch",
|
||||||
"for", "next", "break", "return", "add", "delete",
|
"for", "next", "break", "return", "add", "delete",
|
||||||
"list", "bodylist",
|
"list", "bodylist",
|
||||||
"<init>",
|
"<init>", "hook",
|
||||||
"null",
|
"null",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -933,6 +933,52 @@ bool EventStmt::DoUnserialize(UnserialInfo* info)
|
||||||
return event_expr != 0;
|
return event_expr != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HookStmt::HookStmt(CallExpr* arg_e) : ExprStmt(STMT_HOOK, arg_e)
|
||||||
|
{
|
||||||
|
call_expr = arg_e;
|
||||||
|
}
|
||||||
|
|
||||||
|
Val* HookStmt::Exec(Frame* f, stmt_flow_type& flow) const
|
||||||
|
{
|
||||||
|
RegisterAccess();
|
||||||
|
|
||||||
|
Val* ret = call_expr->Eval(f);
|
||||||
|
Unref(ret);
|
||||||
|
|
||||||
|
flow = FLOW_NEXT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraversalCode HookStmt::Traverse(TraversalCallback* cb) const
|
||||||
|
{
|
||||||
|
TraversalCode tc = cb->PreStmt(this);
|
||||||
|
HANDLE_TC_STMT_PRE(tc);
|
||||||
|
|
||||||
|
// call expr is stored in base class's "e" field.
|
||||||
|
tc = e->Traverse(cb);
|
||||||
|
HANDLE_TC_STMT_PRE(tc);
|
||||||
|
|
||||||
|
tc = cb->PostStmt(this);
|
||||||
|
HANDLE_TC_STMT_POST(tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_SERIAL(HookStmt, SER_HOOK_STMT);
|
||||||
|
|
||||||
|
bool HookStmt::DoSerialize(SerialInfo* info) const
|
||||||
|
{
|
||||||
|
DO_SERIALIZE(SER_HOOK_STMT, ExprStmt);
|
||||||
|
return call_expr->Serialize(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HookStmt::DoUnserialize(UnserialInfo* info)
|
||||||
|
{
|
||||||
|
DO_UNSERIALIZE(ExprStmt);
|
||||||
|
|
||||||
|
call_expr = (CallExpr*) Expr::Unserialize(info, EXPR_CALL);
|
||||||
|
return call_expr != 0;
|
||||||
|
}
|
||||||
|
|
||||||
ForStmt::ForStmt(id_list* arg_loop_vars, Expr* loop_expr)
|
ForStmt::ForStmt(id_list* arg_loop_vars, Expr* loop_expr)
|
||||||
: ExprStmt(STMT_FOR, loop_expr)
|
: ExprStmt(STMT_FOR, loop_expr)
|
||||||
{
|
{
|
||||||
|
@ -1944,6 +1990,7 @@ int same_stmt(const Stmt* s1, const Stmt* s2)
|
||||||
case STMT_RETURN:
|
case STMT_RETURN:
|
||||||
case STMT_EXPR:
|
case STMT_EXPR:
|
||||||
case STMT_EVENT:
|
case STMT_EVENT:
|
||||||
|
case STMT_HOOK:
|
||||||
{
|
{
|
||||||
const ExprStmt* e1 = (const ExprStmt*) s1;
|
const ExprStmt* e1 = (const ExprStmt*) s1;
|
||||||
const ExprStmt* e2 = (const ExprStmt*) s2;
|
const ExprStmt* e2 = (const ExprStmt*) s2;
|
||||||
|
|
18
src/Stmt.h
18
src/Stmt.h
|
@ -286,6 +286,24 @@ protected:
|
||||||
EventExpr* event_expr;
|
EventExpr* event_expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class HookStmt : public ExprStmt {
|
||||||
|
public:
|
||||||
|
HookStmt(CallExpr* e);
|
||||||
|
|
||||||
|
Val* Exec(Frame* f, stmt_flow_type& flow) const;
|
||||||
|
|
||||||
|
TraversalCode Traverse(TraversalCallback* cb) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class Stmt;
|
||||||
|
|
||||||
|
HookStmt() { call_expr = 0; }
|
||||||
|
|
||||||
|
DECLARE_SERIAL(HookStmt);
|
||||||
|
|
||||||
|
CallExpr* call_expr;
|
||||||
|
};
|
||||||
|
|
||||||
class ForStmt : public ExprStmt {
|
class ForStmt : public ExprStmt {
|
||||||
public:
|
public:
|
||||||
ForStmt(id_list* loop_vars, Expr* loop_expr);
|
ForStmt(id_list* loop_vars, Expr* loop_expr);
|
||||||
|
|
|
@ -15,7 +15,7 @@ typedef enum {
|
||||||
STMT_RETURN,
|
STMT_RETURN,
|
||||||
STMT_ADD, STMT_DELETE,
|
STMT_ADD, STMT_DELETE,
|
||||||
STMT_LIST, STMT_EVENT_BODY_LIST,
|
STMT_LIST, STMT_EVENT_BODY_LIST,
|
||||||
STMT_INIT,
|
STMT_INIT, STMT_HOOK,
|
||||||
STMT_NULL
|
STMT_NULL
|
||||||
#define NUM_STMTS (int(STMT_NULL) + 1)
|
#define NUM_STMTS (int(STMT_NULL) + 1)
|
||||||
} BroStmtTag;
|
} BroStmtTag;
|
||||||
|
|
78
src/Type.cc
78
src/Type.cc
|
@ -651,12 +651,12 @@ bool SetType::DoUnserialize(UnserialInfo* info)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FuncType::FuncType(RecordType* arg_args, BroType* arg_yield, int arg_is_event)
|
FuncType::FuncType(RecordType* arg_args, BroType* arg_yield, function_flavor arg_flavor)
|
||||||
: BroType(TYPE_FUNC)
|
: BroType(TYPE_FUNC)
|
||||||
{
|
{
|
||||||
args = arg_args;
|
args = arg_args;
|
||||||
yield = arg_yield;
|
yield = arg_yield;
|
||||||
is_event = arg_is_event;
|
flavor = arg_flavor;
|
||||||
|
|
||||||
arg_types = new TypeList();
|
arg_types = new TypeList();
|
||||||
|
|
||||||
|
@ -664,6 +664,25 @@ FuncType::FuncType(RecordType* arg_args, BroType* arg_yield, int arg_is_event)
|
||||||
arg_types->Append(args->FieldType(i)->Ref());
|
arg_types->Append(args->FieldType(i)->Ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string FuncType::FlavorString() const
|
||||||
|
{
|
||||||
|
switch ( flavor ) {
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_FUNCTION:
|
||||||
|
return "function";
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_EVENT:
|
||||||
|
return "event";
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_HOOK:
|
||||||
|
return "hook";
|
||||||
|
|
||||||
|
default:
|
||||||
|
reporter->InternalError("Invalid function flavor");
|
||||||
|
return "invalid_func_flavor";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FuncType::~FuncType()
|
FuncType::~FuncType()
|
||||||
{
|
{
|
||||||
Unref(arg_types);
|
Unref(arg_types);
|
||||||
|
@ -698,7 +717,7 @@ void FuncType::Describe(ODesc* d) const
|
||||||
{
|
{
|
||||||
if ( d->IsReadable() )
|
if ( d->IsReadable() )
|
||||||
{
|
{
|
||||||
d->Add(is_event ? "event" : "function");
|
d->Add(FlavorString());
|
||||||
d->Add("(");
|
d->Add("(");
|
||||||
args->DescribeFields(d);
|
args->DescribeFields(d);
|
||||||
d->Add(")");
|
d->Add(")");
|
||||||
|
@ -712,7 +731,7 @@ void FuncType::Describe(ODesc* d) const
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
d->Add(int(Tag()));
|
d->Add(int(Tag()));
|
||||||
d->Add(is_event);
|
d->Add(flavor);
|
||||||
d->Add(yield != 0);
|
d->Add(yield != 0);
|
||||||
args->DescribeFields(d);
|
args->DescribeFields(d);
|
||||||
if ( yield )
|
if ( yield )
|
||||||
|
@ -723,7 +742,7 @@ void FuncType::Describe(ODesc* d) const
|
||||||
void FuncType::DescribeReST(ODesc* d) const
|
void FuncType::DescribeReST(ODesc* d) const
|
||||||
{
|
{
|
||||||
d->Add(":bro:type:`");
|
d->Add(":bro:type:`");
|
||||||
d->Add(is_event ? "event" : "function");
|
d->Add(FlavorString());
|
||||||
d->Add("`");
|
d->Add("`");
|
||||||
d->Add(" (");
|
d->Add(" (");
|
||||||
args->DescribeFieldsReST(d, true);
|
args->DescribeFieldsReST(d, true);
|
||||||
|
@ -755,9 +774,30 @@ bool FuncType::DoSerialize(SerialInfo* info) const
|
||||||
|
|
||||||
SERIALIZE_OPTIONAL(yield);
|
SERIALIZE_OPTIONAL(yield);
|
||||||
|
|
||||||
|
int ser_flavor = 0;
|
||||||
|
|
||||||
|
switch ( flavor ) {
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_FUNCTION:
|
||||||
|
ser_flavor = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_EVENT:
|
||||||
|
ser_flavor = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_HOOK:
|
||||||
|
ser_flavor = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
reporter->InternalError("Invalid function flavor serialization");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return args->Serialize(info) &&
|
return args->Serialize(info) &&
|
||||||
arg_types->Serialize(info) &&
|
arg_types->Serialize(info) &&
|
||||||
SERIALIZE(is_event);
|
SERIALIZE(ser_flavor);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FuncType::DoUnserialize(UnserialInfo* info)
|
bool FuncType::DoUnserialize(UnserialInfo* info)
|
||||||
|
@ -774,7 +814,27 @@ bool FuncType::DoUnserialize(UnserialInfo* info)
|
||||||
if ( ! arg_types )
|
if ( ! arg_types )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return UNSERIALIZE(&is_event);
|
int ser_flavor = 0;
|
||||||
|
|
||||||
|
if ( ! UNSERIALIZE(&ser_flavor) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch ( ser_flavor ) {
|
||||||
|
case 0:
|
||||||
|
flavor = FUNC_FLAVOR_FUNCTION;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
flavor = FUNC_FLAVOR_EVENT;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
flavor = FUNC_FLAVOR_HOOK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reporter->InternalError("Invalid function flavor unserialization");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeDecl::TypeDecl(BroType* t, const char* i, attr_list* arg_attrs, bool in_record)
|
TypeDecl::TypeDecl(BroType* t, const char* i, attr_list* arg_attrs, bool in_record)
|
||||||
|
@ -1603,7 +1663,7 @@ int same_type(const BroType* t1, const BroType* t2, int is_init)
|
||||||
const FuncType* ft1 = (const FuncType*) t1;
|
const FuncType* ft1 = (const FuncType*) t1;
|
||||||
const FuncType* ft2 = (const FuncType*) t2;
|
const FuncType* ft2 = (const FuncType*) t2;
|
||||||
|
|
||||||
if ( ft1->IsEvent() != ft2->IsEvent() )
|
if ( ft1->Flavor() != ft2->Flavor() )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ( t1->YieldType() || t2->YieldType() )
|
if ( t1->YieldType() || t2->YieldType() )
|
||||||
|
@ -1890,7 +1950,7 @@ BroType* merge_types(const BroType* t1, const BroType* t2)
|
||||||
BroType* yield = t1->YieldType() ?
|
BroType* yield = t1->YieldType() ?
|
||||||
merge_types(t1->YieldType(), t2->YieldType()) : 0;
|
merge_types(t1->YieldType(), t2->YieldType()) : 0;
|
||||||
|
|
||||||
return new FuncType(args->AsRecordType(), yield, ft1->IsEvent());
|
return new FuncType(args->AsRecordType(), yield, ft1->Flavor());
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_RECORD:
|
case TYPE_RECORD:
|
||||||
|
|
22
src/Type.h
22
src/Type.h
|
@ -35,7 +35,11 @@ typedef enum {
|
||||||
#define NUM_TYPES (int(TYPE_ERROR) + 1)
|
#define NUM_TYPES (int(TYPE_ERROR) + 1)
|
||||||
} TypeTag;
|
} TypeTag;
|
||||||
|
|
||||||
typedef enum { FUNC_FLAVOR_FUNCTION, FUNC_FLAVOR_EVENT } function_flavor;
|
typedef enum {
|
||||||
|
FUNC_FLAVOR_FUNCTION,
|
||||||
|
FUNC_FLAVOR_EVENT,
|
||||||
|
FUNC_FLAVOR_HOOK
|
||||||
|
} function_flavor;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TYPE_INTERNAL_VOID,
|
TYPE_INTERNAL_VOID,
|
||||||
|
@ -350,18 +354,19 @@ protected:
|
||||||
|
|
||||||
class FuncType : public BroType {
|
class FuncType : public BroType {
|
||||||
public:
|
public:
|
||||||
FuncType(RecordType* args, BroType* yield, int is_event);
|
FuncType(RecordType* args, BroType* yield, function_flavor f);
|
||||||
|
|
||||||
~FuncType();
|
~FuncType();
|
||||||
|
|
||||||
RecordType* Args() const { return args; }
|
RecordType* Args() const { return args; }
|
||||||
BroType* YieldType();
|
BroType* YieldType();
|
||||||
void SetYieldType(BroType* arg_yield) { yield = arg_yield; }
|
void SetYieldType(BroType* arg_yield) { yield = arg_yield; }
|
||||||
int IsEvent() const { return is_event; }
|
function_flavor Flavor() const { return flavor; }
|
||||||
|
string FlavorString() const;
|
||||||
|
|
||||||
// Used to convert a function type to an event type.
|
// Used to convert a function type to an event or hook type.
|
||||||
void ClearYieldType()
|
void ClearYieldType(function_flavor arg_flav)
|
||||||
{ Unref(yield); yield = 0; is_event = 1; }
|
{ Unref(yield); yield = 0; flavor = arg_flav; }
|
||||||
|
|
||||||
int MatchesIndex(ListExpr*& index) const;
|
int MatchesIndex(ListExpr*& index) const;
|
||||||
int CheckArgs(const type_list* args) const;
|
int CheckArgs(const type_list* args) const;
|
||||||
|
@ -374,14 +379,13 @@ public:
|
||||||
void DescribeReST(ODesc* d) const;
|
void DescribeReST(ODesc* d) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FuncType() { args = 0; arg_types = 0; yield = 0; return_value = 0; }
|
FuncType() { args = 0; arg_types = 0; yield = 0; flavor = FUNC_FLAVOR_FUNCTION; }
|
||||||
DECLARE_SERIAL(FuncType)
|
DECLARE_SERIAL(FuncType)
|
||||||
|
|
||||||
RecordType* args;
|
RecordType* args;
|
||||||
TypeList* arg_types;
|
TypeList* arg_types;
|
||||||
BroType* yield;
|
BroType* yield;
|
||||||
int is_event;
|
function_flavor flavor;
|
||||||
ID* return_value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TypeType : public BroType {
|
class TypeType : public BroType {
|
||||||
|
|
36
src/Var.cc
36
src/Var.cc
|
@ -171,7 +171,9 @@ static void make_var(ID* id, BroType* t, init_class c, Expr* init,
|
||||||
|
|
||||||
id->UpdateValAttrs();
|
id->UpdateValAttrs();
|
||||||
|
|
||||||
if ( t && t->Tag() == TYPE_FUNC && t->AsFuncType()->IsEvent() )
|
if ( t && t->Tag() == TYPE_FUNC &&
|
||||||
|
(t->AsFuncType()->Flavor() == FUNC_FLAVOR_EVENT ||
|
||||||
|
t->AsFuncType()->Flavor() == FUNC_FLAVOR_HOOK) )
|
||||||
{
|
{
|
||||||
// For events, add a function value (without any body) here so that
|
// For events, add a function value (without any body) here so that
|
||||||
// we can later access the ID even if no implementations have been
|
// we can later access the ID even if no implementations have been
|
||||||
|
@ -258,7 +260,7 @@ void add_type(ID* id, BroType* t, attr_list* attr, int /* is_event */)
|
||||||
case TYPE_FUNC:
|
case TYPE_FUNC:
|
||||||
tnew = new FuncType(t->AsFuncType()->Args(),
|
tnew = new FuncType(t->AsFuncType()->Args(),
|
||||||
t->AsFuncType()->YieldType(),
|
t->AsFuncType()->YieldType(),
|
||||||
t->AsFuncType()->IsEvent());
|
t->AsFuncType()->Flavor());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
SerializationFormat* form = new BinarySerializationFormat();
|
SerializationFormat* form = new BinarySerializationFormat();
|
||||||
|
@ -292,14 +294,14 @@ void add_type(ID* id, BroType* t, attr_list* attr, int /* is_event */)
|
||||||
void begin_func(ID* id, const char* module_name, function_flavor flavor,
|
void begin_func(ID* id, const char* module_name, function_flavor flavor,
|
||||||
int is_redef, FuncType* t)
|
int is_redef, FuncType* t)
|
||||||
{
|
{
|
||||||
if ( flavor == FUNC_FLAVOR_EVENT )
|
if ( flavor == FUNC_FLAVOR_EVENT || flavor == FUNC_FLAVOR_HOOK )
|
||||||
{
|
{
|
||||||
const BroType* yt = t->YieldType();
|
const BroType* yt = t->YieldType();
|
||||||
|
|
||||||
if ( yt && yt->Tag() != TYPE_VOID )
|
if ( yt && yt->Tag() != TYPE_VOID )
|
||||||
id->Error("event cannot yield a value", t);
|
id->Error("event/hook cannot yield a value", t);
|
||||||
|
|
||||||
t->ClearYieldType();
|
t->ClearYieldType(flavor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( id->Type() )
|
if ( id->Type() )
|
||||||
|
@ -313,20 +315,28 @@ void begin_func(ID* id, const char* module_name, function_flavor flavor,
|
||||||
|
|
||||||
if ( id->HasVal() )
|
if ( id->HasVal() )
|
||||||
{
|
{
|
||||||
int id_is_event = id->ID_Val()->AsFunc()->IsEvent();
|
function_flavor id_flavor = id->ID_Val()->AsFunc()->Flavor();
|
||||||
|
|
||||||
if ( id_is_event != (flavor == FUNC_FLAVOR_EVENT) )
|
if ( id_flavor != flavor )
|
||||||
id->Error("inconsistency between event and function", t);
|
id->Error("inconsistent function flavor", t);
|
||||||
if ( id_is_event )
|
|
||||||
{
|
switch ( id_flavor ) {
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_EVENT:
|
||||||
|
case FUNC_FLAVOR_HOOK:
|
||||||
if ( is_redef )
|
if ( is_redef )
|
||||||
// Clear out value so it will be replaced.
|
// Clear out value so it will be replaced.
|
||||||
id->SetVal(0);
|
id->SetVal(0);
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
case FUNC_FLAVOR_FUNCTION:
|
||||||
if ( ! id->IsRedefinable() )
|
if ( ! id->IsRedefinable() )
|
||||||
id->Error("already defined");
|
id->Error("already defined");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
reporter->InternalError("invalid function flavor");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -393,7 +393,7 @@ bool Manager::CreateEventStream(RecordVal* fval)
|
||||||
|
|
||||||
bool allow_file_func = false;
|
bool allow_file_func = false;
|
||||||
|
|
||||||
if ( ! etype->IsEvent() )
|
if ( etype->Flavor() != FUNC_FLAVOR_EVENT )
|
||||||
{
|
{
|
||||||
reporter->Error("stream event is a function, not an event");
|
reporter->Error("stream event is a function, not an event");
|
||||||
return false;
|
return false;
|
||||||
|
@ -566,7 +566,7 @@ bool Manager::CreateTableStream(RecordVal* fval)
|
||||||
{
|
{
|
||||||
FuncType* etype = event->FType()->AsFuncType();
|
FuncType* etype = event->FType()->AsFuncType();
|
||||||
|
|
||||||
if ( ! etype->IsEvent() )
|
if ( etype->Flavor() != FUNC_FLAVOR_EVENT )
|
||||||
{
|
{
|
||||||
reporter->Error("stream event is a function, not an event");
|
reporter->Error("stream event is a function, not an event");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -327,7 +327,7 @@ bool Manager::CreateStream(EnumVal* id, RecordVal* sval)
|
||||||
// Make sure the event is prototyped as expected.
|
// Make sure the event is prototyped as expected.
|
||||||
FuncType* etype = event->FType()->AsFuncType();
|
FuncType* etype = event->FType()->AsFuncType();
|
||||||
|
|
||||||
if ( ! etype->IsEvent() )
|
if ( etype->Flavor() != FUNC_FLAVOR_EVENT )
|
||||||
{
|
{
|
||||||
reporter->Error("stream event is a function, not an event");
|
reporter->Error("stream event is a function, not an event");
|
||||||
return false;
|
return false;
|
||||||
|
|
66
src/parse.y
66
src/parse.y
|
@ -2,14 +2,14 @@
|
||||||
// See the file "COPYING" in the main distribution directory for copyright.
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%expect 87
|
%expect 88
|
||||||
|
|
||||||
%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_ATIFDEF TOK_ATIFNDEF
|
||||||
%token TOK_BOOL TOK_BREAK TOK_CASE TOK_CONST
|
%token TOK_BOOL TOK_BREAK TOK_CASE TOK_CONST
|
||||||
%token TOK_CONSTANT TOK_COPY TOK_COUNT TOK_COUNTER TOK_DEFAULT TOK_DELETE
|
%token TOK_CONSTANT TOK_COPY TOK_COUNT TOK_COUNTER TOK_DEFAULT TOK_DELETE
|
||||||
%token TOK_DOUBLE TOK_ELSE TOK_ENUM TOK_EVENT TOK_EXPORT TOK_FILE TOK_FOR
|
%token TOK_DOUBLE TOK_ELSE TOK_ENUM TOK_EVENT TOK_EXPORT TOK_FILE TOK_FOR
|
||||||
%token TOK_FUNCTION TOK_GLOBAL TOK_ID TOK_IF TOK_INT
|
%token TOK_FUNCTION TOK_GLOBAL TOK_HOOK TOK_ID TOK_IF TOK_INT
|
||||||
%token TOK_INTERVAL TOK_LIST TOK_LOCAL TOK_MODULE
|
%token TOK_INTERVAL TOK_LIST TOK_LOCAL TOK_MODULE
|
||||||
%token TOK_NEXT TOK_OF TOK_PATTERN TOK_PATTERN_TEXT
|
%token TOK_NEXT TOK_OF TOK_PATTERN TOK_PATTERN_TEXT
|
||||||
%token TOK_PORT TOK_PRINT TOK_RECORD TOK_REDEF
|
%token TOK_PORT TOK_PRINT TOK_RECORD TOK_REDEF
|
||||||
|
@ -56,6 +56,7 @@
|
||||||
%type <re> pattern
|
%type <re> pattern
|
||||||
%type <expr> expr init anonymous_function
|
%type <expr> expr init anonymous_function
|
||||||
%type <event_expr> event
|
%type <event_expr> event
|
||||||
|
%type <call_expr> hook
|
||||||
%type <stmt> stmt stmt_list func_body for_head
|
%type <stmt> stmt stmt_list func_body for_head
|
||||||
%type <type> type opt_type enum_body
|
%type <type> type opt_type enum_body
|
||||||
%type <func_type> func_hdr func_params
|
%type <func_type> func_hdr func_params
|
||||||
|
@ -118,6 +119,7 @@ extern const char* g_curr_debug_error;
|
||||||
|
|
||||||
#define YYLTYPE yyltype
|
#define YYLTYPE yyltype
|
||||||
|
|
||||||
|
static bool in_hook = false;
|
||||||
int in_init = 0;
|
int in_init = 0;
|
||||||
int in_record = 0;
|
int in_record = 0;
|
||||||
bool resolving_global_ID = false;
|
bool resolving_global_ID = false;
|
||||||
|
@ -868,7 +870,13 @@ type:
|
||||||
| TOK_EVENT '(' formal_args ')'
|
| TOK_EVENT '(' formal_args ')'
|
||||||
{
|
{
|
||||||
set_location(@1, @3);
|
set_location(@1, @3);
|
||||||
$$ = new FuncType($3, 0, 1);
|
$$ = new FuncType($3, 0, FUNC_FLAVOR_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
| TOK_HOOK '(' formal_args ')'
|
||||||
|
{
|
||||||
|
set_location(@1, @3);
|
||||||
|
$$ = new FuncType($3, 0, FUNC_FLAVOR_HOOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_FILE TOK_OF type
|
| TOK_FILE TOK_OF type
|
||||||
|
@ -1000,12 +1008,27 @@ decl:
|
||||||
ID* id = $2;
|
ID* id = $2;
|
||||||
if ( id->Type()->Tag() == TYPE_FUNC )
|
if ( id->Type()->Tag() == TYPE_FUNC )
|
||||||
{
|
{
|
||||||
if ( id->Type()->AsFuncType()->IsEvent() )
|
switch ( id->Type()->AsFuncType()->Flavor() ) {
|
||||||
current_reST_doc->AddEvent(
|
|
||||||
new BroDocObj(id, reST_doc_comments));
|
case FUNC_FLAVOR_FUNCTION:
|
||||||
else
|
|
||||||
current_reST_doc->AddFunction(
|
current_reST_doc->AddFunction(
|
||||||
new BroDocObj(id, reST_doc_comments));
|
new BroDocObj(id, reST_doc_comments));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_EVENT:
|
||||||
|
current_reST_doc->AddEvent(
|
||||||
|
new BroDocObj(id, reST_doc_comments));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FUNC_FLAVOR_HOOK:
|
||||||
|
current_reST_doc->AddHook(
|
||||||
|
new BroDocObj(id, reST_doc_comments));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
reporter->InternalError("invalid function flavor");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -1185,6 +1208,15 @@ func_hdr:
|
||||||
current_reST_doc->AddEventHandler(
|
current_reST_doc->AddEventHandler(
|
||||||
new BroDocObj($2, reST_doc_comments));
|
new BroDocObj($2, reST_doc_comments));
|
||||||
}
|
}
|
||||||
|
| TOK_HOOK def_global_id func_params
|
||||||
|
{
|
||||||
|
begin_func($2, current_module.c_str(),
|
||||||
|
FUNC_FLAVOR_HOOK, 0, $3);
|
||||||
|
$$ = $3;
|
||||||
|
if ( generate_documentation )
|
||||||
|
current_reST_doc->AddHookHandler(
|
||||||
|
new BroDocObj($2, reST_doc_comments));
|
||||||
|
}
|
||||||
| TOK_REDEF TOK_EVENT event_id func_params
|
| TOK_REDEF TOK_EVENT event_id func_params
|
||||||
{
|
{
|
||||||
begin_func($3, current_module.c_str(),
|
begin_func($3, current_module.c_str(),
|
||||||
|
@ -1219,9 +1251,9 @@ begin_func:
|
||||||
|
|
||||||
func_params:
|
func_params:
|
||||||
'(' formal_args ')' ':' type
|
'(' formal_args ')' ':' type
|
||||||
{ $$ = new FuncType($2, $5, 0); }
|
{ $$ = new FuncType($2, $5, FUNC_FLAVOR_FUNCTION); }
|
||||||
| '(' formal_args ')'
|
| '(' formal_args ')'
|
||||||
{ $$ = new FuncType($2, base_type(TYPE_VOID), 0); }
|
{ $$ = new FuncType($2, base_type(TYPE_VOID), FUNC_FLAVOR_FUNCTION); }
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_type:
|
opt_type:
|
||||||
|
@ -1341,6 +1373,14 @@ stmt:
|
||||||
brofiler.AddStmt($$);
|
brofiler.AddStmt($$);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
| TOK_HOOK { in_hook = true; } hook { in_hook = false; } ';' opt_no_test
|
||||||
|
{
|
||||||
|
set_location(@1, @5);
|
||||||
|
$$ = new HookStmt($3);
|
||||||
|
if ( ! $6 )
|
||||||
|
brofiler.AddStmt($$);
|
||||||
|
}
|
||||||
|
|
||||||
| TOK_IF '(' expr ')' stmt
|
| TOK_IF '(' expr ')' stmt
|
||||||
{
|
{
|
||||||
set_location(@1, @4);
|
set_location(@1, @4);
|
||||||
|
@ -1494,6 +1534,14 @@ event:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
hook:
|
||||||
|
expr '(' opt_expr_list ')'
|
||||||
|
{
|
||||||
|
set_location(@1, @4);
|
||||||
|
$$ = new CallExpr($1, $3, in_hook);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
case_list:
|
case_list:
|
||||||
case_list case
|
case_list case
|
||||||
{ $1->append($2); }
|
{ $1->append($2); }
|
||||||
|
|
|
@ -287,6 +287,7 @@ for return TOK_FOR;
|
||||||
function return TOK_FUNCTION;
|
function return TOK_FUNCTION;
|
||||||
global return TOK_GLOBAL;
|
global return TOK_GLOBAL;
|
||||||
"?$" return TOK_HAS_FIELD;
|
"?$" return TOK_HAS_FIELD;
|
||||||
|
hook return TOK_HOOK;
|
||||||
if return TOK_IF;
|
if return TOK_IF;
|
||||||
in return TOK_IN;
|
in return TOK_IN;
|
||||||
"!"{OWS}in/[^A-Za-z0-9] return TOK_NOT_IN; /* don't confuse w "! infoo"! */
|
"!"{OWS}in/[^A-Za-z0-9] return TOK_NOT_IN; /* don't confuse w "! infoo"! */
|
||||||
|
|
|
@ -73,9 +73,9 @@ Events
|
||||||
|
|
||||||
Functions
|
Functions
|
||||||
#########
|
#########
|
||||||
=============================================== =======================================
|
=================================================== =======================================
|
||||||
:bro:id:`Example::a_function`: :bro:type:`func` Summarize purpose of "a_function" here.
|
:bro:id:`Example::a_function`: :bro:type:`function` Summarize purpose of "a_function" here.
|
||||||
=============================================== =======================================
|
=================================================== =======================================
|
||||||
|
|
||||||
Redefinitions
|
Redefinitions
|
||||||
#############
|
#############
|
||||||
|
|
|
@ -20,9 +20,9 @@ Types
|
||||||
|
|
||||||
Functions
|
Functions
|
||||||
#########
|
#########
|
||||||
===================================== ======================================
|
========================================= ======================================
|
||||||
:bro:id:`test_func`: :bro:type:`func` This is a global function declaration.
|
:bro:id:`test_func`: :bro:type:`function` This is a global function declaration.
|
||||||
===================================== ======================================
|
========================================= ======================================
|
||||||
|
|
||||||
Detailed Interface
|
Detailed Interface
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
7
testing/btest/Baseline/language.hook/out
Normal file
7
testing/btest/Baseline/language.hook/out
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
myhook, &priority=10, [a=1156, b=hello world]
|
||||||
|
myhook, &priority=5, [a=37, b=goobye world]
|
||||||
|
myhook3, 8
|
||||||
|
myhook4, 2
|
||||||
|
myhook4, 1
|
||||||
|
myhook, &priority=10, [a=2, b=it works]
|
||||||
|
myhook, &priority=5, [a=37, b=goobye world]
|
1
testing/btest/Baseline/language.invalid_hook/out
Normal file
1
testing/btest/Baseline/language.invalid_hook/out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
error in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.invalid_hook/invalid_hook.bro, line 15: hook called in expression, use hook statement instead (myhook(nope))
|
78
testing/btest/core/leaks/hook.bro
Normal file
78
testing/btest/core/leaks/hook.bro
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# Needs perftools support.
|
||||||
|
#
|
||||||
|
# @TEST-GROUP: leaks
|
||||||
|
#
|
||||||
|
# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks
|
||||||
|
#
|
||||||
|
# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local bro -m -b -r $TRACES/wikipedia.trace %INPUT
|
||||||
|
|
||||||
|
type rec: record {
|
||||||
|
a: count;
|
||||||
|
b: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
global myhook: hook(r: rec);
|
||||||
|
global myhook2: hook(s: string);
|
||||||
|
# a hook doesn't have to take any arguments
|
||||||
|
global myhook4: hook();
|
||||||
|
|
||||||
|
hook myhook(r: rec) &priority=5
|
||||||
|
{
|
||||||
|
print "myhook, &priority=5", r;
|
||||||
|
# break statement short-circuits the hook handling chain.
|
||||||
|
break;
|
||||||
|
print "ERROR: break statement should return from hook handler body";
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook(r: rec)
|
||||||
|
{
|
||||||
|
# This handler shouldn't execute ever because of the handler at priority=5
|
||||||
|
# exiting the body from a "break" statement.
|
||||||
|
print "myhook, &priority=0", rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook(r: rec) &priority=10
|
||||||
|
{
|
||||||
|
print "myhook, &priority=10", r;
|
||||||
|
# modifications to the record argument will be seen by remaining handlers.
|
||||||
|
r$a = 37;
|
||||||
|
r$b = "goobye world";
|
||||||
|
# returning from the handler early, is fine, remaining handlers still run.
|
||||||
|
return;
|
||||||
|
print "ERROR: break statement should return from hook handler body";
|
||||||
|
}
|
||||||
|
|
||||||
|
# hook function doesn't need a declaration, we can go straight to defining
|
||||||
|
# a handler body.
|
||||||
|
hook myhook3(i: count)
|
||||||
|
{
|
||||||
|
print "myhook3", i;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook4() &priority=1
|
||||||
|
{
|
||||||
|
print "myhook4", 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook4() &priority=2
|
||||||
|
{
|
||||||
|
print "myhook4", 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
event new_connection(c: connection)
|
||||||
|
{
|
||||||
|
print "new_connection", c$id;
|
||||||
|
|
||||||
|
hook myhook([$a=1156, $b="hello world"]);
|
||||||
|
|
||||||
|
# A hook with no handlers is fine, it's just a no-op.
|
||||||
|
hook myhook2("nope");
|
||||||
|
|
||||||
|
hook myhook3(8);
|
||||||
|
hook myhook4();
|
||||||
|
|
||||||
|
# A hook can be treated like other data types and doesn't have to be
|
||||||
|
# invoked directly by name.
|
||||||
|
local h = myhook;
|
||||||
|
hook h([$a=2, $b="it works"]);
|
||||||
|
}
|
|
@ -11,8 +11,8 @@
|
||||||
# @TEST-EXEC: cat receiver/http.log | $SCRIPTS/diff-remove-timestamps >receiver.http.log
|
# @TEST-EXEC: cat receiver/http.log | $SCRIPTS/diff-remove-timestamps >receiver.http.log
|
||||||
# @TEST-EXEC: cmp sender.http.log receiver.http.log
|
# @TEST-EXEC: cmp sender.http.log receiver.http.log
|
||||||
#
|
#
|
||||||
# @TEST-EXEC: bro -x sender/events.bst | sed 's/^Event \[[-0-9.]*\] //g' | grep '^http_' | grep -v http_stats | sed 's/(.*$//g' | $SCRIPTS/diff-remove-timestamps >events.snd.log
|
# @TEST-EXEC: bro -x sender/events.bst | sed 's/^event \[[-0-9.]*\] //g' | grep '^http_' | grep -v http_stats | sed 's/(.*$//g' | $SCRIPTS/diff-remove-timestamps >events.snd.log
|
||||||
# @TEST-EXEC: bro -x receiver/events.bst | sed 's/^Event \[[-0-9.]*\] //g' | grep '^http_' | grep -v http_stats | sed 's/(.*$//g' | $SCRIPTS/diff-remove-timestamps >events.rec.log
|
# @TEST-EXEC: bro -x receiver/events.bst | sed 's/^event \[[-0-9.]*\] //g' | grep '^http_' | grep -v http_stats | sed 's/(.*$//g' | $SCRIPTS/diff-remove-timestamps >events.rec.log
|
||||||
# @TEST-EXEC: btest-diff events.rec.log
|
# @TEST-EXEC: btest-diff events.rec.log
|
||||||
# @TEST-EXEC: btest-diff events.snd.log
|
# @TEST-EXEC: btest-diff events.snd.log
|
||||||
# @TEST-EXEC: cmp events.rec.log events.snd.log
|
# @TEST-EXEC: cmp events.rec.log events.snd.log
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
# @TEST-EXEC: cat receiver/http.log | $SCRIPTS/diff-remove-timestamps >receiver.http.log
|
# @TEST-EXEC: cat receiver/http.log | $SCRIPTS/diff-remove-timestamps >receiver.http.log
|
||||||
# @TEST-EXEC: cmp sender.http.log receiver.http.log
|
# @TEST-EXEC: cmp sender.http.log receiver.http.log
|
||||||
#
|
#
|
||||||
# @TEST-EXEC: bro -x sender/events.bst | sed 's/^Event \[[-0-9.]*\] //g' | grep '^http_' | grep -v http_stats | sed 's/(.*$//g' | $SCRIPTS/diff-remove-timestamps >events.snd.log
|
# @TEST-EXEC: bro -x sender/events.bst | sed 's/^event \[[-0-9.]*\] //g' | grep '^http_' | grep -v http_stats | sed 's/(.*$//g' | $SCRIPTS/diff-remove-timestamps >events.snd.log
|
||||||
# @TEST-EXEC: bro -x receiver/events.bst | sed 's/^Event \[[-0-9.]*\] //g' | grep '^http_' | grep -v http_stats | sed 's/(.*$//g' | $SCRIPTS/diff-remove-timestamps >events.rec.log
|
# @TEST-EXEC: bro -x receiver/events.bst | sed 's/^event \[[-0-9.]*\] //g' | grep '^http_' | grep -v http_stats | sed 's/(.*$//g' | $SCRIPTS/diff-remove-timestamps >events.rec.log
|
||||||
# @TEST-EXEC: btest-diff events.rec.log
|
# @TEST-EXEC: btest-diff events.rec.log
|
||||||
# @TEST-EXEC: btest-diff events.snd.log
|
# @TEST-EXEC: btest-diff events.snd.log
|
||||||
# @TEST-EXEC: cmp events.rec.log events.snd.log
|
# @TEST-EXEC: cmp events.rec.log events.snd.log
|
||||||
|
|
71
testing/btest/language/hook.bro
Normal file
71
testing/btest/language/hook.bro
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# @TEST-EXEC: bro -b %INPUT >out
|
||||||
|
# @TEST-EXEC: btest-diff out
|
||||||
|
|
||||||
|
type rec: record {
|
||||||
|
a: count;
|
||||||
|
b: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
global myhook: hook(r: rec);
|
||||||
|
global myhook2: hook(s: string);
|
||||||
|
# a hook doesn't have to take any arguments
|
||||||
|
global myhook4: hook();
|
||||||
|
|
||||||
|
hook myhook(r: rec) &priority=5
|
||||||
|
{
|
||||||
|
print "myhook, &priority=5", r;
|
||||||
|
# break statement short-circuits the hook handling chain.
|
||||||
|
break;
|
||||||
|
print "ERROR: break statement should return from hook handler body";
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook(r: rec)
|
||||||
|
{
|
||||||
|
# This handler shouldn't execute ever because of the handler at priority=5
|
||||||
|
# exiting the body from a "break" statement.
|
||||||
|
print "myhook, &priority=0", rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook(r: rec) &priority=10
|
||||||
|
{
|
||||||
|
print "myhook, &priority=10", r;
|
||||||
|
# modifications to the record argument will be seen by remaining handlers.
|
||||||
|
r$a = 37;
|
||||||
|
r$b = "goobye world";
|
||||||
|
# returning from the handler early, is fine, remaining handlers still run.
|
||||||
|
return;
|
||||||
|
print "ERROR: break statement should return from hook handler body";
|
||||||
|
}
|
||||||
|
|
||||||
|
# hook function doesn't need a declaration, we can go straight to defining
|
||||||
|
# a handler body.
|
||||||
|
hook myhook3(i: count)
|
||||||
|
{
|
||||||
|
print "myhook3", i;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook4() &priority=1
|
||||||
|
{
|
||||||
|
print "myhook4", 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook myhook4() &priority=2
|
||||||
|
{
|
||||||
|
print "myhook4", 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init()
|
||||||
|
{
|
||||||
|
hook myhook([$a=1156, $b="hello world"]);
|
||||||
|
|
||||||
|
# A hook with no handlers is fine, it's just a no-op.
|
||||||
|
hook myhook2("nope");
|
||||||
|
|
||||||
|
hook myhook3(8);
|
||||||
|
hook myhook4();
|
||||||
|
|
||||||
|
# A hook can be treated like other data types and doesn't have to be
|
||||||
|
# invoked directly by name.
|
||||||
|
local h = myhook;
|
||||||
|
hook h([$a=2, $b="it works"]);
|
||||||
|
}
|
16
testing/btest/language/invalid_hook.bro
Normal file
16
testing/btest/language/invalid_hook.bro
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# @TEST-EXEC-FAIL: bro -b %INPUT >out 2>&1
|
||||||
|
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff out
|
||||||
|
|
||||||
|
global myhook: hook(s: string);
|
||||||
|
|
||||||
|
hook myhook(s: string)
|
||||||
|
{
|
||||||
|
print "myhook", s;
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init()
|
||||||
|
{
|
||||||
|
# hooks must be invoked with a "hook", statement. They have no return
|
||||||
|
# value and don't make sense to evaluate as arbitrary expressions.
|
||||||
|
local r = myhook("nope");
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue