mirror of
https://github.com/zeek/zeek.git
synced 2025-10-10 02:28:21 +00:00
Initial import of svn+ssh:://svn.icir.org/bro/trunk/bro as of r7088
This commit is contained in:
commit
61757ac78b
1383 changed files with 380824 additions and 0 deletions
411
src/Trigger.cc
Normal file
411
src/Trigger.cc
Normal file
|
@ -0,0 +1,411 @@
|
|||
// $Id: Trigger.cc 2359 2005-12-21 23:55:32Z vern $
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Trigger.h"
|
||||
#include "Traverse.h"
|
||||
|
||||
// Callback class to traverse an expression, registering all relevant IDs and
|
||||
// Vals for change notifications.
|
||||
|
||||
class TriggerTraversalCallback : public TraversalCallback {
|
||||
public:
|
||||
TriggerTraversalCallback(Trigger *arg_trigger)
|
||||
{ Ref(arg_trigger); trigger = arg_trigger; }
|
||||
|
||||
~TriggerTraversalCallback()
|
||||
{ Unref(trigger); }
|
||||
|
||||
virtual TraversalCode PreExpr(const Expr*);
|
||||
|
||||
private:
|
||||
Trigger* trigger;
|
||||
};
|
||||
|
||||
TraversalCode TriggerTraversalCallback::PreExpr(const Expr* expr)
|
||||
{
|
||||
// We catch all expressions here which in some way reference global
|
||||
// state.
|
||||
|
||||
switch ( expr->Tag() ) {
|
||||
case EXPR_NAME:
|
||||
{
|
||||
const NameExpr* e = static_cast<const NameExpr*>(expr);
|
||||
if ( e->Id()->IsGlobal() )
|
||||
trigger->Register(e->Id());
|
||||
|
||||
Val* v = e->Id()->ID_Val();
|
||||
if ( v && v->IsMutableVal() )
|
||||
trigger->Register(v);
|
||||
break;
|
||||
};
|
||||
|
||||
case EXPR_INDEX:
|
||||
{
|
||||
const IndexExpr* e = static_cast<const IndexExpr*>(expr);
|
||||
BroObj::SuppressRunTimeErrors no_errors;
|
||||
Val* v = e->Eval(trigger->frame);
|
||||
if ( v )
|
||||
trigger->Register(v);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// All others are uninteresting.
|
||||
break;
|
||||
}
|
||||
|
||||
return TC_CONTINUE;
|
||||
}
|
||||
|
||||
class TriggerTimer : public Timer {
|
||||
public:
|
||||
TriggerTimer(double arg_timeout, Trigger* arg_trigger)
|
||||
: Timer(network_time + arg_timeout, TIMER_TRIGGER)
|
||||
{
|
||||
Ref(arg_trigger);
|
||||
trigger = arg_trigger;
|
||||
timeout = arg_timeout;
|
||||
time = network_time;
|
||||
}
|
||||
|
||||
~TriggerTimer()
|
||||
{ Unref(trigger); }
|
||||
|
||||
void Dispatch(double t, int is_expire)
|
||||
{
|
||||
// The network_time may still have been zero when the
|
||||
// timer was instantiated. In this case, it fires
|
||||
// immediately and we simply restart it.
|
||||
if ( time )
|
||||
trigger->Timeout();
|
||||
else
|
||||
{
|
||||
TriggerTimer* timer = new TriggerTimer(timeout, trigger);
|
||||
timer_mgr->Add(timer);
|
||||
trigger->timer = timer;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
Trigger* trigger;
|
||||
double timeout;
|
||||
double time;
|
||||
};
|
||||
|
||||
Trigger::Trigger(Expr* arg_cond, Stmt* arg_body, Stmt* arg_timeout_stmts,
|
||||
Expr* arg_timeout, Frame* arg_frame,
|
||||
bool arg_is_return, const Location* arg_location)
|
||||
{
|
||||
if ( ! pending )
|
||||
pending = new list<Trigger*>;
|
||||
|
||||
cond = arg_cond;
|
||||
body = arg_body;
|
||||
timeout_stmts = arg_timeout_stmts;
|
||||
timeout = arg_timeout;
|
||||
frame = arg_frame->Clone();
|
||||
timer = 0;
|
||||
delayed = false;
|
||||
disabled = false;
|
||||
attached = 0;
|
||||
is_return = arg_is_return;
|
||||
location = arg_location;
|
||||
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: instantiating", Name());
|
||||
|
||||
if ( is_return )
|
||||
{
|
||||
Trigger* parent = frame->GetTrigger();
|
||||
if ( ! parent )
|
||||
{
|
||||
run_time("return trigger in context which does not allow delaying result");
|
||||
Unref(this);
|
||||
return;
|
||||
}
|
||||
|
||||
parent->Attach(this);
|
||||
arg_frame->SetDelayed();
|
||||
}
|
||||
|
||||
Val* timeout = arg_timeout ? arg_timeout->ExprVal() : 0;
|
||||
|
||||
if ( ! Eval() && timeout )
|
||||
{
|
||||
timer = new TriggerTimer(timeout->AsInterval(), this);
|
||||
timer_mgr->Add(timer);
|
||||
}
|
||||
}
|
||||
|
||||
Trigger::~Trigger()
|
||||
{
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: deleting", Name());
|
||||
|
||||
for ( ValCache::iterator i = cache.begin(); i != cache.end(); ++i )
|
||||
Unref(i->second);
|
||||
|
||||
Unref(frame);
|
||||
UnregisterAll();
|
||||
|
||||
Unref(attached);
|
||||
// Due to ref'counting, "this" cannot be part of pending at this
|
||||
// point.
|
||||
}
|
||||
|
||||
void Trigger::Init()
|
||||
{
|
||||
assert(! disabled);
|
||||
UnregisterAll();
|
||||
TriggerTraversalCallback cb(this);
|
||||
cond->Traverse(&cb);
|
||||
}
|
||||
|
||||
Trigger::TriggerList* Trigger::pending = 0;
|
||||
|
||||
bool Trigger::Eval()
|
||||
{
|
||||
if ( disabled )
|
||||
return true;
|
||||
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: evaluating", Name());
|
||||
|
||||
if ( delayed )
|
||||
{
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: skipping eval due to delayed call",
|
||||
Name());
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's unfortunate that we have to copy the frame again here but
|
||||
// otherwise changes to any of the locals would propagate to later
|
||||
// evaluations.
|
||||
//
|
||||
// An alternative approach to copying the frame would be to deep-copy
|
||||
// the expression itself, replacing all references to locals with
|
||||
// constants.
|
||||
Frame* f = frame->Clone();
|
||||
f->SetTrigger(this);
|
||||
Val* v = cond->Eval(f);
|
||||
f->ClearTrigger();
|
||||
|
||||
if ( f->HasDelayed() )
|
||||
{
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: eval has delayed", Name());
|
||||
assert(!v);
|
||||
Unref(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( v->IsZero() )
|
||||
{
|
||||
// Not true. Perhaps next time...
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: trigger condition is false", Name());
|
||||
Unref(v);
|
||||
Unref(f);
|
||||
Init();
|
||||
return false;
|
||||
}
|
||||
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: trigger condition is true, executing",
|
||||
Name());
|
||||
|
||||
Unref(v);
|
||||
stmt_flow_type flow;
|
||||
v = body->Exec(f, flow);
|
||||
|
||||
if ( is_return )
|
||||
{
|
||||
Trigger* trigger = frame->GetTrigger();
|
||||
assert(trigger);
|
||||
assert(frame->GetCall());
|
||||
assert(trigger->attached == this);
|
||||
|
||||
#ifdef DEBUG
|
||||
const char* pname = copy_string(trigger->Name());
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: trigger has parent %s, caching result", Name(), pname);
|
||||
delete [] pname;
|
||||
#endif
|
||||
|
||||
trigger->Cache(frame->GetCall(), v);
|
||||
trigger->Release();
|
||||
}
|
||||
|
||||
Unref(v);
|
||||
Unref(f);
|
||||
|
||||
if ( timer )
|
||||
timer_mgr->Cancel(timer);
|
||||
|
||||
Disable();
|
||||
Unref(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Trigger::QueueTrigger(Trigger* trigger)
|
||||
{
|
||||
assert(! trigger->disabled);
|
||||
assert(pending);
|
||||
if ( std::find(pending->begin(), pending->end(), trigger) == pending->end() )
|
||||
{
|
||||
Ref(trigger);
|
||||
pending->push_back(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
void Trigger::EvaluatePending()
|
||||
{
|
||||
DBG_LOG(DBG_NOTIFIERS, "evaluating all pending triggers");
|
||||
|
||||
if ( ! pending )
|
||||
return;
|
||||
|
||||
// While we iterate over the list, executing statements, we may
|
||||
// in fact trigger new triggers and thereby modify the list.
|
||||
// Therefore, we create a new temporary list which will receive
|
||||
// triggers triggered during this time.
|
||||
TriggerList* orig = pending;
|
||||
TriggerList tmp;
|
||||
pending = &tmp;
|
||||
|
||||
for ( TriggerList::iterator i = orig->begin(); i != orig->end(); ++i )
|
||||
{
|
||||
Trigger* t = *i;
|
||||
(*i)->Eval();
|
||||
Unref(t);
|
||||
}
|
||||
|
||||
pending = orig;
|
||||
orig->clear();
|
||||
|
||||
// Sigh... Is this really better than a for-loop?
|
||||
std::copy(tmp.begin(), tmp.end(),
|
||||
insert_iterator<TriggerList>(*pending, pending->begin()));
|
||||
}
|
||||
|
||||
void Trigger::Timeout()
|
||||
{
|
||||
if ( disabled )
|
||||
return;
|
||||
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: timeout", Name());
|
||||
if ( timeout_stmts )
|
||||
{
|
||||
stmt_flow_type flow;
|
||||
Frame* f = frame->Clone();
|
||||
Val* v = timeout_stmts->Exec(f, flow);
|
||||
|
||||
if ( is_return )
|
||||
{
|
||||
Trigger* trigger = frame->GetTrigger();
|
||||
assert(trigger);
|
||||
assert(frame->GetCall());
|
||||
assert(trigger->attached == this);
|
||||
|
||||
#ifdef DEBUG
|
||||
const char* pname = copy_string(trigger->Name());
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: trigger has parent %s, caching timeout result", Name(), pname);
|
||||
delete [] pname;
|
||||
#endif
|
||||
trigger->Cache(frame->GetCall(), v);
|
||||
trigger->Release();
|
||||
}
|
||||
|
||||
Unref(v);
|
||||
Unref(f);
|
||||
}
|
||||
|
||||
Disable();
|
||||
Unref(this);
|
||||
}
|
||||
|
||||
void Trigger::Register(ID* id)
|
||||
{
|
||||
assert(! disabled);
|
||||
notifiers.Register(id, this);
|
||||
|
||||
Ref(id);
|
||||
ids.insert(id);
|
||||
}
|
||||
|
||||
void Trigger::Register(Val* val)
|
||||
{
|
||||
assert(! disabled);
|
||||
notifiers.Register(val, this);
|
||||
|
||||
Ref(val);
|
||||
vals.insert(val);
|
||||
}
|
||||
|
||||
void Trigger::UnregisterAll()
|
||||
{
|
||||
loop_over_list(ids, i)
|
||||
{
|
||||
notifiers.Unregister(ids[i], this);
|
||||
Unref(ids[i]);
|
||||
}
|
||||
|
||||
ids.clear();
|
||||
|
||||
loop_over_list(vals, j)
|
||||
{
|
||||
notifiers.Unregister(vals[j], this);
|
||||
Unref(vals[j]);
|
||||
}
|
||||
|
||||
vals.clear();
|
||||
}
|
||||
|
||||
void Trigger::Attach(Trigger *trigger)
|
||||
{
|
||||
assert(! disabled);
|
||||
assert(! trigger->disabled);
|
||||
assert(! trigger->delayed);
|
||||
|
||||
#ifdef DEBUG
|
||||
const char* pname = copy_string(trigger->Name());
|
||||
DBG_LOG(DBG_NOTIFIERS, "%s: attaching to %s", Name(), pname);
|
||||
delete [] pname;
|
||||
#endif
|
||||
|
||||
Ref(trigger);
|
||||
attached = trigger;
|
||||
Hold();
|
||||
}
|
||||
|
||||
void Trigger::Cache(const CallExpr* expr, Val* v)
|
||||
{
|
||||
if ( disabled )
|
||||
return;
|
||||
|
||||
ValCache::iterator i = cache.find(expr);
|
||||
|
||||
if ( i != cache.end() )
|
||||
{
|
||||
Unref(i->second);
|
||||
i->second = v;
|
||||
}
|
||||
|
||||
else
|
||||
cache.insert(ValCache::value_type(expr, v));
|
||||
|
||||
Ref(v);
|
||||
|
||||
QueueTrigger(this);
|
||||
}
|
||||
|
||||
|
||||
Val* Trigger::Lookup(const CallExpr* expr)
|
||||
{
|
||||
assert(! disabled);
|
||||
|
||||
ValCache::iterator i = cache.find(expr);
|
||||
return (i != cache.end()) ? i->second : 0;
|
||||
}
|
||||
|
||||
const char* Trigger::Name()
|
||||
{
|
||||
assert(location);
|
||||
return fmt("%s:%d-%d", location->filename,
|
||||
location->first_line, location->last_line);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue