mirror of
https://github.com/zeek/zeek.git
synced 2025-10-05 16:18:19 +00:00
Merge branch 'master' of https://github.com/zeek/zeek
This commit is contained in:
commit
f95390aabe
24 changed files with 388 additions and 1424 deletions
23
CHANGES
23
CHANGES
|
@ -1,10 +1,31 @@
|
||||||
|
|
||||||
2.6-487 | 2019-06-20 20:38:26 -0700
|
2.6-500 | 2019-06-20 20:54:15 -0700
|
||||||
|
|
||||||
* Add new RDP event: rdp_client_cluster_data (Jeff Atkinson)
|
* Add new RDP event: rdp_client_cluster_data (Jeff Atkinson)
|
||||||
|
|
||||||
* Added "options" field to RDP::ClientChannelDef (Jeff Atkinson)
|
* Added "options" field to RDP::ClientChannelDef (Jeff Atkinson)
|
||||||
|
|
||||||
|
2.6-494 | 2019-06-20 20:24:38 -0700
|
||||||
|
|
||||||
|
* Renaming src/StateAccess.{h,cc} to src/Notifier.{h,cc}.
|
||||||
|
|
||||||
|
The old names did not reflect the content of the files anymore. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
* Remove MutableVal, StateAccess classes, enum Opcode. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
* Redo API for notifiers.
|
||||||
|
|
||||||
|
There's now an notifier::Modifiable interface class that class
|
||||||
|
supposed to signal modifications are to be derived from. This takes
|
||||||
|
the place of the former MutableValue class and also unifies how Val
|
||||||
|
and IDs signal modifications. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
* Redo NotfifierRegistry to no longer rely on StateAccess.
|
||||||
|
|
||||||
|
We simplify the API to a simple Modified() operation. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
|
* Add new test for when-statement watching global variables. (Robin Sommer, Corelight)
|
||||||
|
|
||||||
2.6-482 | 2019-06-20 19:57:20 -0700
|
2.6-482 | 2019-06-20 19:57:20 -0700
|
||||||
|
|
||||||
* Make configure complain if submodules are not checked out. (Johanna Amann, Corelight)
|
* Make configure complain if submodules are not checked out. (Johanna Amann, Corelight)
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
2.6-487
|
2.6-500
|
||||||
|
|
|
@ -302,7 +302,7 @@ set(bro_SRCS
|
||||||
Scope.cc
|
Scope.cc
|
||||||
SerializationFormat.cc
|
SerializationFormat.cc
|
||||||
Sessions.cc
|
Sessions.cc
|
||||||
StateAccess.cc
|
Notifier.cc
|
||||||
Stats.cc
|
Stats.cc
|
||||||
Stmt.cc
|
Stmt.cc
|
||||||
Tag.cc
|
Tag.cc
|
||||||
|
|
|
@ -12,7 +12,7 @@ DebugLogger debug_logger;
|
||||||
// Same order here as in DebugStream.
|
// Same order here as in DebugStream.
|
||||||
DebugLogger::Stream DebugLogger::streams[NUM_DBGS] = {
|
DebugLogger::Stream DebugLogger::streams[NUM_DBGS] = {
|
||||||
{ "serial", 0, false }, { "rules", 0, false },
|
{ "serial", 0, false }, { "rules", 0, false },
|
||||||
{ "state", 0, false }, {"string", 0, false },
|
{ "string", 0, false },
|
||||||
{ "notifiers", 0, false }, { "main-loop", 0, false },
|
{ "notifiers", 0, false }, { "main-loop", 0, false },
|
||||||
{ "dpd", 0, false }, { "tm", 0, false },
|
{ "dpd", 0, false }, { "tm", 0, false },
|
||||||
{ "logging", 0, false }, {"input", 0, false },
|
{ "logging", 0, false }, {"input", 0, false },
|
||||||
|
|
|
@ -16,9 +16,8 @@
|
||||||
enum DebugStream {
|
enum DebugStream {
|
||||||
DBG_SERIAL, // Serialization
|
DBG_SERIAL, // Serialization
|
||||||
DBG_RULES, // Signature matching
|
DBG_RULES, // Signature matching
|
||||||
DBG_STATE, // StateAccess logging
|
|
||||||
DBG_STRING, // String code
|
DBG_STRING, // String code
|
||||||
DBG_NOTIFIERS, // Notifiers (see StateAccess.h)
|
DBG_NOTIFIERS, // Notifiers
|
||||||
DBG_MAINLOOP, // Main IOSource loop
|
DBG_MAINLOOP, // Main IOSource loop
|
||||||
DBG_ANALYZER, // Analyzer framework
|
DBG_ANALYZER, // Analyzer framework
|
||||||
DBG_TM, // Time-machine packet input via Brocolli
|
DBG_TM, // Time-machine packet input via Brocolli
|
||||||
|
|
34
src/Expr.cc
34
src/Expr.cc
|
@ -97,7 +97,7 @@ void Expr::EvalIntoAggregate(const BroType* /* t */, Val* /* aggr */,
|
||||||
Internal("Expr::EvalIntoAggregate called");
|
Internal("Expr::EvalIntoAggregate called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Expr::Assign(Frame* /* f */, Val* /* v */, Opcode /* op */)
|
void Expr::Assign(Frame* /* f */, Val* /* v */)
|
||||||
{
|
{
|
||||||
Internal("Expr::Assign called");
|
Internal("Expr::Assign called");
|
||||||
}
|
}
|
||||||
|
@ -261,10 +261,10 @@ Expr* NameExpr::MakeLvalue()
|
||||||
return new RefExpr(this);
|
return new RefExpr(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NameExpr::Assign(Frame* f, Val* v, Opcode op)
|
void NameExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
if ( id->IsGlobal() )
|
if ( id->IsGlobal() )
|
||||||
id->SetVal(v, op);
|
id->SetVal(v);
|
||||||
else
|
else
|
||||||
f->SetElement(id->Offset(), v);
|
f->SetElement(id->Offset(), v);
|
||||||
}
|
}
|
||||||
|
@ -1007,18 +1007,18 @@ Val* IncrExpr::Eval(Frame* f) const
|
||||||
if ( elt )
|
if ( elt )
|
||||||
{
|
{
|
||||||
Val* new_elt = DoSingleEval(f, elt);
|
Val* new_elt = DoSingleEval(f, elt);
|
||||||
v_vec->Assign(i, new_elt, OP_INCR);
|
v_vec->Assign(i, new_elt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
v_vec->Assign(i, 0, OP_INCR);
|
v_vec->Assign(i, 0);
|
||||||
}
|
}
|
||||||
op->Assign(f, v_vec, OP_INCR);
|
op->Assign(f, v_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Val* old_v = v;
|
Val* old_v = v;
|
||||||
op->Assign(f, v = DoSingleEval(f, old_v), OP_INCR);
|
op->Assign(f, v = DoSingleEval(f, old_v));
|
||||||
Unref(old_v);
|
Unref(old_v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2041,9 +2041,9 @@ Expr* RefExpr::MakeLvalue()
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefExpr::Assign(Frame* f, Val* v, Opcode opcode)
|
void RefExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
op->Assign(f, v, opcode);
|
op->Assign(f, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssignExpr::AssignExpr(Expr* arg_op1, Expr* arg_op2, int arg_is_init,
|
AssignExpr::AssignExpr(Expr* arg_op1, Expr* arg_op2, int arg_is_init,
|
||||||
|
@ -2743,7 +2743,7 @@ Val* IndexExpr::Fold(Val* v1, Val* v2) const
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
|
void IndexExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
if ( IsError() )
|
if ( IsError() )
|
||||||
return;
|
return;
|
||||||
|
@ -2783,7 +2783,7 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
|
||||||
for ( auto idx = 0u; idx < v_vect->Size(); idx++, first++ )
|
for ( auto idx = 0u; idx < v_vect->Size(); idx++, first++ )
|
||||||
v1_vect->Insert(first, v_vect->Lookup(idx)->Ref());
|
v1_vect->Insert(first, v_vect->Lookup(idx)->Ref());
|
||||||
}
|
}
|
||||||
else if ( ! v1_vect->Assign(v2, v, op) )
|
else if ( ! v1_vect->Assign(v2, v) )
|
||||||
{
|
{
|
||||||
if ( v )
|
if ( v )
|
||||||
{
|
{
|
||||||
|
@ -2803,7 +2803,7 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_TABLE:
|
case TYPE_TABLE:
|
||||||
if ( ! v1->AsTableVal()->Assign(v2, v, op) )
|
if ( ! v1->AsTableVal()->Assign(v2, v) )
|
||||||
{
|
{
|
||||||
if ( v )
|
if ( v )
|
||||||
{
|
{
|
||||||
|
@ -2906,7 +2906,7 @@ int FieldExpr::CanDel() const
|
||||||
return td->FindAttr(ATTR_DEFAULT) || td->FindAttr(ATTR_OPTIONAL);
|
return td->FindAttr(ATTR_DEFAULT) || td->FindAttr(ATTR_OPTIONAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldExpr::Assign(Frame* f, Val* v, Opcode opcode)
|
void FieldExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
if ( IsError() )
|
if ( IsError() )
|
||||||
return;
|
return;
|
||||||
|
@ -2915,14 +2915,14 @@ void FieldExpr::Assign(Frame* f, Val* v, Opcode opcode)
|
||||||
if ( op_v )
|
if ( op_v )
|
||||||
{
|
{
|
||||||
RecordVal* r = op_v->AsRecordVal();
|
RecordVal* r = op_v->AsRecordVal();
|
||||||
r->Assign(field, v, opcode);
|
r->Assign(field, v);
|
||||||
Unref(r);
|
Unref(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldExpr::Delete(Frame* f)
|
void FieldExpr::Delete(Frame* f)
|
||||||
{
|
{
|
||||||
Assign(f, 0, OP_ASSIGN_IDX);
|
Assign(f, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Val* FieldExpr::Fold(Val* v) const
|
Val* FieldExpr::Fold(Val* v) const
|
||||||
|
@ -4753,7 +4753,7 @@ Expr* ListExpr::MakeLvalue()
|
||||||
return new RefExpr(this);
|
return new RefExpr(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListExpr::Assign(Frame* f, Val* v, Opcode op)
|
void ListExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
ListVal* lv = v->AsListVal();
|
ListVal* lv = v->AsListVal();
|
||||||
|
|
||||||
|
@ -4761,7 +4761,7 @@ void ListExpr::Assign(Frame* f, Val* v, Opcode op)
|
||||||
RuntimeError("mismatch in list lengths");
|
RuntimeError("mismatch in list lengths");
|
||||||
|
|
||||||
loop_over_list(exprs, i)
|
loop_over_list(exprs, i)
|
||||||
exprs[i]->Assign(f, (*lv->Vals())[i]->Ref(), op);
|
exprs[i]->Assign(f, (*lv->Vals())[i]->Ref());
|
||||||
|
|
||||||
Unref(lv);
|
Unref(lv);
|
||||||
}
|
}
|
||||||
|
|
12
src/Expr.h
12
src/Expr.h
|
@ -86,7 +86,7 @@ public:
|
||||||
const;
|
const;
|
||||||
|
|
||||||
// Assign to the given value, if appropriate.
|
// Assign to the given value, if appropriate.
|
||||||
virtual void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN);
|
virtual void Assign(Frame* f, Val* v);
|
||||||
|
|
||||||
// Returns the type corresponding to this expression interpreted
|
// Returns the type corresponding to this expression interpreted
|
||||||
// as an initialization. The type should be Unref()'d when done
|
// as an initialization. The type should be Unref()'d when done
|
||||||
|
@ -239,7 +239,7 @@ public:
|
||||||
ID* Id() const { return id; }
|
ID* Id() const { return id; }
|
||||||
|
|
||||||
Val* Eval(Frame* f) const override;
|
Val* Eval(Frame* f) const override;
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
int IsPure() const override;
|
int IsPure() const override;
|
||||||
|
|
||||||
|
@ -586,7 +586,7 @@ class RefExpr : public UnaryExpr {
|
||||||
public:
|
public:
|
||||||
explicit RefExpr(Expr* op);
|
explicit RefExpr(Expr* op);
|
||||||
|
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -639,7 +639,7 @@ public:
|
||||||
void Add(Frame* f) override;
|
void Add(Frame* f) override;
|
||||||
void Delete(Frame* f) override;
|
void Delete(Frame* f) override;
|
||||||
|
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
|
|
||||||
// Need to override Eval since it can take a vector arg but does
|
// Need to override Eval since it can take a vector arg but does
|
||||||
|
@ -671,7 +671,7 @@ public:
|
||||||
|
|
||||||
int CanDel() const override;
|
int CanDel() const override;
|
||||||
|
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
void Delete(Frame* f) override;
|
void Delete(Frame* f) override;
|
||||||
|
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
|
@ -991,7 +991,7 @@ public:
|
||||||
BroType* InitType() const override;
|
BroType* InitType() const override;
|
||||||
Val* InitVal(const BroType* t, Val* aggr) const override;
|
Val* InitVal(const BroType* t, Val* aggr) const override;
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
|
|
||||||
TraversalCode Traverse(TraversalCallback* cb) const override;
|
TraversalCode Traverse(TraversalCallback* cb) const override;
|
||||||
|
|
||||||
|
|
44
src/ID.cc
44
src/ID.cc
|
@ -59,34 +59,14 @@ void ID::ClearVal()
|
||||||
val = 0;
|
val = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ID::SetVal(Val* v, Opcode op, bool arg_weak_ref)
|
void ID::SetVal(Val* v, bool arg_weak_ref)
|
||||||
{
|
{
|
||||||
if ( op != OP_NONE )
|
|
||||||
{
|
|
||||||
MutableVal::Properties props = 0;
|
|
||||||
|
|
||||||
if ( attrs && attrs->FindAttr(ATTR_TRACKED) )
|
|
||||||
props |= MutableVal::TRACKED;
|
|
||||||
|
|
||||||
if ( props )
|
|
||||||
{
|
|
||||||
if ( v->IsMutableVal() )
|
|
||||||
v->AsMutableVal()->AddProperties(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef DEBUG
|
|
||||||
if ( props )
|
|
||||||
#else
|
|
||||||
if ( debug_logger.IsVerbose() || props )
|
|
||||||
#endif
|
|
||||||
StateAccess::Log(new StateAccess(op, this, v, val));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! weak_ref )
|
if ( ! weak_ref )
|
||||||
Unref(val);
|
Unref(val);
|
||||||
|
|
||||||
val = v;
|
val = v;
|
||||||
weak_ref = arg_weak_ref;
|
weak_ref = arg_weak_ref;
|
||||||
|
Modified();
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
UpdateValID();
|
UpdateValID();
|
||||||
|
@ -175,16 +155,6 @@ void ID::UpdateValAttrs()
|
||||||
if ( ! attrs )
|
if ( ! attrs )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MutableVal::Properties props = 0;
|
|
||||||
|
|
||||||
if ( val && val->IsMutableVal() )
|
|
||||||
{
|
|
||||||
if ( attrs->FindAttr(ATTR_TRACKED) )
|
|
||||||
props |= MutableVal::TRACKED;
|
|
||||||
|
|
||||||
val->AsMutableVal()->AddProperties(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( val && val->Type()->Tag() == TYPE_TABLE )
|
if ( val && val->Type()->Tag() == TYPE_TABLE )
|
||||||
val->AsTableVal()->SetAttrs(attrs);
|
val->AsTableVal()->SetAttrs(attrs);
|
||||||
|
|
||||||
|
@ -242,16 +212,6 @@ void ID::RemoveAttr(attr_tag a)
|
||||||
{
|
{
|
||||||
if ( attrs )
|
if ( attrs )
|
||||||
attrs->RemoveAttr(a);
|
attrs->RemoveAttr(a);
|
||||||
|
|
||||||
if ( val && val->IsMutableVal() )
|
|
||||||
{
|
|
||||||
MutableVal::Properties props = 0;
|
|
||||||
|
|
||||||
if ( a == ATTR_TRACKED )
|
|
||||||
props |= MutableVal::TRACKED;
|
|
||||||
|
|
||||||
val->AsMutableVal()->RemoveProperties(props);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ID::SetOption()
|
void ID::SetOption()
|
||||||
|
|
10
src/ID.h
10
src/ID.h
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "Type.h"
|
#include "Type.h"
|
||||||
#include "Attr.h"
|
#include "Attr.h"
|
||||||
#include "StateAccess.h"
|
#include "Notifier.h"
|
||||||
#include "TraverseTypes.h"
|
#include "TraverseTypes.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class Func;
|
||||||
typedef enum { INIT_NONE, INIT_FULL, INIT_EXTRA, INIT_REMOVE, } init_class;
|
typedef enum { INIT_NONE, INIT_FULL, INIT_EXTRA, INIT_REMOVE, } init_class;
|
||||||
typedef enum { SCOPE_FUNCTION, SCOPE_MODULE, SCOPE_GLOBAL } IDScope;
|
typedef enum { SCOPE_FUNCTION, SCOPE_MODULE, SCOPE_GLOBAL } IDScope;
|
||||||
|
|
||||||
class ID : public BroObj {
|
class ID : public BroObj, public notifier::Modifiable {
|
||||||
public:
|
public:
|
||||||
ID(const char* name, IDScope arg_scope, bool arg_is_export);
|
ID(const char* name, IDScope arg_scope, bool arg_is_export);
|
||||||
~ID() override;
|
~ID() override;
|
||||||
|
@ -46,7 +46,7 @@ public:
|
||||||
// reference to the Val, the Val will be destroyed (naturally,
|
// reference to the Val, the Val will be destroyed (naturally,
|
||||||
// you have to take care that it will not be accessed via
|
// you have to take care that it will not be accessed via
|
||||||
// the ID afterwards).
|
// the ID afterwards).
|
||||||
void SetVal(Val* v, Opcode op = OP_ASSIGN, bool weak_ref = false);
|
void SetVal(Val* v, bool weak_ref = false);
|
||||||
|
|
||||||
void SetVal(Val* v, init_class c);
|
void SetVal(Val* v, init_class c);
|
||||||
void SetVal(Expr* ev, init_class c);
|
void SetVal(Expr* ev, init_class c);
|
||||||
|
@ -70,10 +70,6 @@ public:
|
||||||
|
|
||||||
bool IsRedefinable() const { return FindAttr(ATTR_REDEF) != 0; }
|
bool IsRedefinable() const { return FindAttr(ATTR_REDEF) != 0; }
|
||||||
|
|
||||||
// Returns true if ID is one of those internal globally unique IDs
|
|
||||||
// to which MutableVals are bound (there name start with a '#').
|
|
||||||
bool IsInternalGlobal() const { return name && name[0] == '#'; }
|
|
||||||
|
|
||||||
void SetAttrs(Attributes* attr);
|
void SetAttrs(Attributes* attr);
|
||||||
void AddAttrs(Attributes* attr);
|
void AddAttrs(Attributes* attr);
|
||||||
void RemoveAttr(attr_tag a);
|
void RemoveAttr(attr_tag a);
|
||||||
|
|
72
src/Notifier.cc
Normal file
72
src/Notifier.cc
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
|
||||||
|
#include "DebugLogger.h"
|
||||||
|
#include "Notifier.h"
|
||||||
|
|
||||||
|
notifier::Registry notifier::registry;
|
||||||
|
|
||||||
|
notifier::Receiver::Receiver()
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "creating receiver %p", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifier::Receiver::~Receiver()
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "deleting receiver %p", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifier::Registry::~Registry()
|
||||||
|
{
|
||||||
|
while ( registrations.begin() != registrations.end() )
|
||||||
|
Unregister(registrations.begin()->first);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier::Registry::Register(Modifiable* m, notifier::Receiver* r)
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "registering object %p for receiver %p", m, r);
|
||||||
|
|
||||||
|
registrations.insert({m, r});
|
||||||
|
++m->num_receivers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier::Registry::Unregister(Modifiable* m, notifier::Receiver* r)
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "unregistering object %p from receiver %p", m, r);
|
||||||
|
|
||||||
|
auto x = registrations.equal_range(m);
|
||||||
|
for ( auto i = x.first; i != x.second; i++ )
|
||||||
|
{
|
||||||
|
if ( i->second == r )
|
||||||
|
{
|
||||||
|
--i->first->num_receivers;
|
||||||
|
registrations.erase(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier::Registry::Unregister(Modifiable* m)
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "unregistering object %p from all notifiers", m);
|
||||||
|
|
||||||
|
auto x = registrations.equal_range(m);
|
||||||
|
for ( auto i = x.first; i != x.second; i++ )
|
||||||
|
--i->first->num_receivers;
|
||||||
|
|
||||||
|
registrations.erase(x.first, x.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier::Registry::Modified(Modifiable* m)
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "object %p has been modified", m);
|
||||||
|
|
||||||
|
auto x = registrations.equal_range(m);
|
||||||
|
for ( auto i = x.first; i != x.second; i++ )
|
||||||
|
i->second->Modified(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifier::Modifiable::~Modifiable()
|
||||||
|
{
|
||||||
|
if ( num_receivers )
|
||||||
|
registry.Unregister(this);
|
||||||
|
}
|
116
src/Notifier.h
Normal file
116
src/Notifier.h
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
//
|
||||||
|
// A notification framework to inform interested parties of modifications to
|
||||||
|
// selected global objects. To get notified about a change, derive a class
|
||||||
|
// from notifier::Receiver and register the interesting objects with the
|
||||||
|
// notification::Registry.
|
||||||
|
|
||||||
|
#ifndef NOTIFIER_H
|
||||||
|
#define NOTIFIER_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "DebugLogger.h"
|
||||||
|
|
||||||
|
namespace notifier {
|
||||||
|
|
||||||
|
class Modifiable;
|
||||||
|
|
||||||
|
/** Interface class for receivers of notifications. */
|
||||||
|
class Receiver {
|
||||||
|
public:
|
||||||
|
Receiver();
|
||||||
|
virtual ~Receiver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback executed when a register object has been modified.
|
||||||
|
*
|
||||||
|
* @param m object that was modified
|
||||||
|
*/
|
||||||
|
virtual void Modified(Modifiable* m) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Singleton class tracking all notification requests globally. */
|
||||||
|
class Registry {
|
||||||
|
public:
|
||||||
|
~Registry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a receiver to be informed when a modifiable object has
|
||||||
|
* changed.
|
||||||
|
*
|
||||||
|
* @param m object to track. Does not take ownership, but the object
|
||||||
|
* will automatically unregister itself on destruction.
|
||||||
|
*
|
||||||
|
* @param r receiver to notify on changes. Does not take ownershop,
|
||||||
|
* the receiver must remain valid as long as the registration stays
|
||||||
|
* in place.
|
||||||
|
*/
|
||||||
|
void Register(Modifiable* m, Receiver* r);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels a receiver's request to be informed about an object's
|
||||||
|
* modification. The arguments to the method must match what was
|
||||||
|
* originally registered.
|
||||||
|
*
|
||||||
|
* @param m object to no loger track.
|
||||||
|
*
|
||||||
|
* @param r receiver to no longer notify.
|
||||||
|
*/
|
||||||
|
void Unregister(Modifiable* m, Receiver* Receiver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels any active receiver requests to be informed about a
|
||||||
|
* partilar object's modifications.
|
||||||
|
*
|
||||||
|
* @param m object to no loger track.
|
||||||
|
*/
|
||||||
|
void Unregister(Modifiable* m);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Modifiable;
|
||||||
|
|
||||||
|
// Inform all registered receivers of a modification to an object.
|
||||||
|
// Will be called from the object itself.
|
||||||
|
void Modified(Modifiable* m);
|
||||||
|
|
||||||
|
typedef std::unordered_multimap<Modifiable*, Receiver*> ModifiableMap;
|
||||||
|
ModifiableMap registrations;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton object tracking all global notification requests.
|
||||||
|
*/
|
||||||
|
extern Registry registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for objects that can trigger notifications to receivers when
|
||||||
|
* modified.
|
||||||
|
*/
|
||||||
|
class Modifiable {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Calling this method signals to all registered receivers that the
|
||||||
|
* object has been modified.
|
||||||
|
*/
|
||||||
|
void Modified()
|
||||||
|
{
|
||||||
|
if ( num_receivers )
|
||||||
|
registry.Modified(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class Registry;
|
||||||
|
|
||||||
|
virtual ~Modifiable();
|
||||||
|
|
||||||
|
// Number of currently registered receivers.
|
||||||
|
uint64 num_receivers = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
23
src/Op.h
23
src/Op.h
|
@ -1,23 +0,0 @@
|
||||||
// See the file "COPYING" in the main distribution directory for copyright.
|
|
||||||
|
|
||||||
#ifndef op_h
|
|
||||||
#define op_h
|
|
||||||
|
|
||||||
// BRO operations.
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
OP_INCR, OP_DECR, OP_NOT, OP_NEGATE,
|
|
||||||
OP_PLUS, OP_MINUS, OP_TIMES, OP_DIVIDE, OP_MOD,
|
|
||||||
OP_AND, OP_OR,
|
|
||||||
OP_LT, OP_LE, OP_EQ, OP_NE, OP_GE, OP_GT,
|
|
||||||
OP_MATCH,
|
|
||||||
OP_ASSIGN,
|
|
||||||
OP_INDEX, OP_FIELD,
|
|
||||||
OP_IN,
|
|
||||||
OP_LIST,
|
|
||||||
OP_CALL,
|
|
||||||
OP_SCHED,
|
|
||||||
OP_NAME, OP_CONST, OP_THIS
|
|
||||||
} BroOP;
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,643 +0,0 @@
|
||||||
#include "Val.h"
|
|
||||||
#include "StateAccess.h"
|
|
||||||
#include "Event.h"
|
|
||||||
#include "NetVar.h"
|
|
||||||
#include "DebugLogger.h"
|
|
||||||
|
|
||||||
int StateAccess::replaying = 0;
|
|
||||||
|
|
||||||
StateAccess::StateAccess(Opcode arg_opcode,
|
|
||||||
const MutableVal* arg_target, const Val* arg_op1,
|
|
||||||
const Val* arg_op2, const Val* arg_op3)
|
|
||||||
{
|
|
||||||
opcode = arg_opcode;
|
|
||||||
target.val = const_cast<MutableVal*>(arg_target);
|
|
||||||
target_type = TYPE_MVAL;
|
|
||||||
op1.val = const_cast<Val*>(arg_op1);
|
|
||||||
op1_type = TYPE_VAL;
|
|
||||||
op2 = const_cast<Val*>(arg_op2);
|
|
||||||
op3 = const_cast<Val*>(arg_op3);
|
|
||||||
delete_op1_key = false;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::StateAccess(Opcode arg_opcode,
|
|
||||||
const ID* arg_target, const Val* arg_op1,
|
|
||||||
const Val* arg_op2, const Val* arg_op3)
|
|
||||||
{
|
|
||||||
opcode = arg_opcode;
|
|
||||||
target.id = const_cast<ID*>(arg_target);
|
|
||||||
target_type = TYPE_ID;
|
|
||||||
op1.val = const_cast<Val*>(arg_op1);
|
|
||||||
op1_type = TYPE_VAL;
|
|
||||||
op2 = const_cast<Val*>(arg_op2);
|
|
||||||
op3 = const_cast<Val*>(arg_op3);
|
|
||||||
delete_op1_key = false;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::StateAccess(Opcode arg_opcode,
|
|
||||||
const ID* arg_target, const HashKey* arg_op1,
|
|
||||||
const Val* arg_op2, const Val* arg_op3)
|
|
||||||
{
|
|
||||||
opcode = arg_opcode;
|
|
||||||
target.id = const_cast<ID*>(arg_target);
|
|
||||||
target_type = TYPE_ID;
|
|
||||||
op1.key = new HashKey(arg_op1->Key(), arg_op1->Size(), arg_op1->Hash());
|
|
||||||
op1_type = TYPE_KEY;
|
|
||||||
op2 = const_cast<Val*>(arg_op2);
|
|
||||||
op3 = const_cast<Val*>(arg_op3);
|
|
||||||
delete_op1_key = true;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::StateAccess(Opcode arg_opcode,
|
|
||||||
const MutableVal* arg_target, const HashKey* arg_op1,
|
|
||||||
const Val* arg_op2, const Val* arg_op3)
|
|
||||||
{
|
|
||||||
opcode = arg_opcode;
|
|
||||||
target.val = const_cast<MutableVal*>(arg_target);
|
|
||||||
target_type = TYPE_MVAL;
|
|
||||||
op1.key = new HashKey(arg_op1->Key(), arg_op1->Size(), arg_op1->Hash());
|
|
||||||
op1_type = TYPE_KEY;
|
|
||||||
op2 = const_cast<Val*>(arg_op2);
|
|
||||||
op3 = const_cast<Val*>(arg_op3);
|
|
||||||
delete_op1_key = true;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::StateAccess(const StateAccess& sa)
|
|
||||||
{
|
|
||||||
opcode = sa.opcode;
|
|
||||||
target_type = sa.target_type;
|
|
||||||
op1_type = sa.op1_type;
|
|
||||||
delete_op1_key = false;
|
|
||||||
|
|
||||||
if ( target_type == TYPE_ID )
|
|
||||||
target.id = sa.target.id;
|
|
||||||
else
|
|
||||||
target.val = sa.target.val;
|
|
||||||
|
|
||||||
if ( op1_type == TYPE_VAL )
|
|
||||||
op1.val = sa.op1.val;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We need to copy the key as the pointer may not be
|
|
||||||
// valid anymore later.
|
|
||||||
op1.key = new HashKey(sa.op1.key->Key(), sa.op1.key->Size(),
|
|
||||||
sa.op1.key->Hash());
|
|
||||||
delete_op1_key = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
op2 = sa.op2;
|
|
||||||
op3 = sa.op3;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::~StateAccess()
|
|
||||||
{
|
|
||||||
if ( target_type == TYPE_ID )
|
|
||||||
Unref(target.id);
|
|
||||||
else
|
|
||||||
Unref(target.val);
|
|
||||||
|
|
||||||
if ( op1_type == TYPE_VAL )
|
|
||||||
Unref(op1.val);
|
|
||||||
else if ( delete_op1_key )
|
|
||||||
delete op1.key;
|
|
||||||
|
|
||||||
Unref(op2);
|
|
||||||
Unref(op3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StateAccess::RefThem()
|
|
||||||
{
|
|
||||||
if ( target_type == TYPE_ID )
|
|
||||||
Ref(target.id);
|
|
||||||
else
|
|
||||||
Ref(target.val);
|
|
||||||
|
|
||||||
if ( op1_type == TYPE_VAL && op1.val )
|
|
||||||
Ref(op1.val);
|
|
||||||
|
|
||||||
if ( op2 )
|
|
||||||
Ref(op2);
|
|
||||||
if ( op3 )
|
|
||||||
Ref(op3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Val* GetInteger(bro_int_t n, TypeTag t)
|
|
||||||
{
|
|
||||||
if ( t == TYPE_INT )
|
|
||||||
return val_mgr->GetInt(n);
|
|
||||||
|
|
||||||
return val_mgr->GetCount(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StateAccess::Replay()
|
|
||||||
{
|
|
||||||
// For simplicity we assume that we only replay unserialized accesses.
|
|
||||||
assert(target_type == TYPE_ID && op1_type == TYPE_VAL);
|
|
||||||
|
|
||||||
if ( ! target.id )
|
|
||||||
return;
|
|
||||||
|
|
||||||
Val* v = target.id->ID_Val();
|
|
||||||
TypeTag t = v ? v->Type()->Tag() : TYPE_VOID;
|
|
||||||
|
|
||||||
if ( opcode != OP_ASSIGN && ! v )
|
|
||||||
{
|
|
||||||
// FIXME: I think this warrants an internal error,
|
|
||||||
// but let's check that first ...
|
|
||||||
// reporter->InternalError("replay id lacking a value");
|
|
||||||
reporter->Error("replay id lacks a value");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
++replaying;
|
|
||||||
|
|
||||||
switch ( opcode ) {
|
|
||||||
case OP_ASSIGN:
|
|
||||||
assert(op1.val);
|
|
||||||
// There mustn't be a direct assignment to a unique ID.
|
|
||||||
assert(target.id->Name()[0] != '#');
|
|
||||||
|
|
||||||
target.id->SetVal(op1.val->Ref());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_INCR:
|
|
||||||
if ( IsIntegral(t) )
|
|
||||||
{
|
|
||||||
assert(op1.val && op2);
|
|
||||||
// We derive the amount as difference between old
|
|
||||||
// and new value.
|
|
||||||
bro_int_t amount =
|
|
||||||
op1.val->CoerceToInt() - op2->CoerceToInt();
|
|
||||||
|
|
||||||
target.id->SetVal(GetInteger(v->CoerceToInt() + amount, t),
|
|
||||||
OP_INCR);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_ASSIGN_IDX:
|
|
||||||
assert(op1.val);
|
|
||||||
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
assert(op2);
|
|
||||||
v->AsTableVal()->Assign(op1.val, op2 ? op2->Ref() : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( t == TYPE_RECORD )
|
|
||||||
{
|
|
||||||
const char* field = op1.val->AsString()->CheckString();
|
|
||||||
int idx = v->Type()->AsRecordType()->FieldOffset(field);
|
|
||||||
|
|
||||||
if ( idx >= 0 )
|
|
||||||
v->AsRecordVal()->Assign(idx, op2 ? op2->Ref() : 0);
|
|
||||||
else
|
|
||||||
reporter->Error("access replay: unknown record field %s for assign", field);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( t == TYPE_VECTOR )
|
|
||||||
{
|
|
||||||
assert(op2);
|
|
||||||
bro_uint_t index = op1.val->AsCount();
|
|
||||||
v->AsVectorVal()->Assign(index, op2 ? op2->Ref() : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
reporter->InternalError("unknown type in replaying index assign");
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_INCR_IDX:
|
|
||||||
{
|
|
||||||
assert(op1.val && op2 && op3);
|
|
||||||
|
|
||||||
// We derive the amount as the difference between old
|
|
||||||
// and new value.
|
|
||||||
bro_int_t amount = op2->CoerceToInt() - op3->CoerceToInt();
|
|
||||||
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
t = v->Type()->AsTableType()->YieldType()->Tag();
|
|
||||||
Val* lookup_op1 = v->AsTableVal()->Lookup(op1.val);
|
|
||||||
int delta = lookup_op1->CoerceToInt() + amount;
|
|
||||||
Val* new_val = GetInteger(delta, t);
|
|
||||||
v->AsTableVal()->Assign(op1.val, new_val, OP_INCR );
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( t == TYPE_RECORD )
|
|
||||||
{
|
|
||||||
const char* field = op1.val->AsString()->CheckString();
|
|
||||||
int idx = v->Type()->AsRecordType()->FieldOffset(field);
|
|
||||||
if ( idx >= 0 )
|
|
||||||
{
|
|
||||||
t = v->Type()->AsRecordType()->FieldType(idx)->Tag();
|
|
||||||
Val* lookup_field =
|
|
||||||
v->AsRecordVal()->Lookup(idx);
|
|
||||||
bro_int_t delta =
|
|
||||||
lookup_field->CoerceToInt() + amount;
|
|
||||||
Val* new_val = GetInteger(delta, t);
|
|
||||||
v->AsRecordVal()->Assign(idx, new_val, OP_INCR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
reporter->Error("access replay: unknown record field %s for assign", field);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( t == TYPE_VECTOR )
|
|
||||||
{
|
|
||||||
bro_uint_t index = op1.val->AsCount();
|
|
||||||
t = v->Type()->AsVectorType()->YieldType()->Tag();
|
|
||||||
Val* lookup_op1 = v->AsVectorVal()->Lookup(index);
|
|
||||||
int delta = lookup_op1->CoerceToInt() + amount;
|
|
||||||
Val* new_val = GetInteger(delta, t);
|
|
||||||
v->AsVectorVal()->Assign(index, new_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
reporter->InternalError("unknown type in replaying index increment");
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OP_ADD:
|
|
||||||
assert(op1.val);
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
v->AsTableVal()->Assign(op1.val, 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_DEL:
|
|
||||||
assert(op1.val);
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
Unref(v->AsTableVal()->Delete(op1.val));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_EXPIRE:
|
|
||||||
assert(op1.val);
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
// No old check for expire. It may have already
|
|
||||||
// been deleted by ourselves. Furthermore, we
|
|
||||||
// ignore the expire_func's return value.
|
|
||||||
TableVal* tv = v->AsTableVal();
|
|
||||||
if ( tv->Lookup(op1.val, false) )
|
|
||||||
{
|
|
||||||
// We want to propagate state updates which
|
|
||||||
// are performed in the expire_func.
|
|
||||||
StateAccess::ResumeReplay();
|
|
||||||
|
|
||||||
tv->CallExpireFunc(op1.val->Ref());
|
|
||||||
|
|
||||||
StateAccess::SuspendReplay();
|
|
||||||
|
|
||||||
Unref(tv->AsTableVal()->Delete(op1.val));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_PRINT:
|
|
||||||
assert(op1.val);
|
|
||||||
reporter->InternalError("access replay for print not implemented");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_READ_IDX:
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
assert(op1.val);
|
|
||||||
TableVal* tv = v->AsTableVal();
|
|
||||||
|
|
||||||
// Update the timestamp if we have a read_expire.
|
|
||||||
if ( tv->FindAttr(ATTR_EXPIRE_READ) )
|
|
||||||
{
|
|
||||||
tv->UpdateTimestamp(op1.val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
reporter->Error("read for non-table");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
reporter->InternalError("access replay: unknown opcode for StateAccess");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
--replaying;
|
|
||||||
}
|
|
||||||
|
|
||||||
ID* StateAccess::Target() const
|
|
||||||
{
|
|
||||||
return target_type == TYPE_ID ? target.id : target.val->UniqueID();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StateAccess::Describe(ODesc* d) const
|
|
||||||
{
|
|
||||||
const ID* id;
|
|
||||||
const char* id_str = "";
|
|
||||||
const char* unique_str = "";
|
|
||||||
|
|
||||||
d->SetShort();
|
|
||||||
|
|
||||||
if ( target_type == TYPE_ID )
|
|
||||||
{
|
|
||||||
id = target.id;
|
|
||||||
|
|
||||||
if ( ! id )
|
|
||||||
{
|
|
||||||
d->Add("(unknown id)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
id_str = id->Name();
|
|
||||||
|
|
||||||
if ( id->ID_Val() && id->ID_Val()->IsMutableVal() &&
|
|
||||||
id->Name()[0] != '#' )
|
|
||||||
unique_str = fmt(" [id] (%s)", id->ID_Val()->AsMutableVal()->UniqueID()->Name());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
id = target.val->UniqueID();
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
if ( target.val->GetID() )
|
|
||||||
{
|
|
||||||
id_str = target.val->GetID()->Name();
|
|
||||||
unique_str = fmt(" [val] (%s)", id->Name());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
id_str = id->Name();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Val* op1 = op1_type == TYPE_VAL ?
|
|
||||||
this->op1.val :
|
|
||||||
id->ID_Val()->AsTableVal()->RecoverIndex(this->op1.key);
|
|
||||||
|
|
||||||
switch ( opcode ) {
|
|
||||||
case OP_ASSIGN:
|
|
||||||
assert(op1);
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add(" = ");
|
|
||||||
op1->Describe(d);
|
|
||||||
if ( op2 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op2->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_INCR:
|
|
||||||
assert(op1 && op2);
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add(" += ");
|
|
||||||
d->Add(op1->CoerceToInt() - op2->CoerceToInt());
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_ASSIGN_IDX:
|
|
||||||
assert(op1);
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
d->Add(" = ");
|
|
||||||
if ( op2 )
|
|
||||||
op2->Describe(d);
|
|
||||||
else
|
|
||||||
d->Add("(null)");
|
|
||||||
if ( op3 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op3->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_INCR_IDX:
|
|
||||||
assert(op1 && op2 && op3);
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
d->Add(" += ");
|
|
||||||
d->Add(op2->CoerceToInt() - op3->CoerceToInt());
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_ADD:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("add ");
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
if ( op2 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op2->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_DEL:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("del ");
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
if ( op2 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op2->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_EXPIRE:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("expire ");
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
if ( op2 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op2->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_PRINT:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("print ");
|
|
||||||
d->Add(id_str);
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_READ_IDX:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("read ");
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
reporter->InternalError("unknown opcode for StateAccess");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( op1_type != TYPE_VAL )
|
|
||||||
Unref(const_cast<Val*>(op1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void StateAccess::Log(StateAccess* access)
|
|
||||||
{
|
|
||||||
bool tracked = false;
|
|
||||||
|
|
||||||
if ( access->target_type == TYPE_ID )
|
|
||||||
{
|
|
||||||
if ( access->target.id->FindAttr(ATTR_TRACKED) )
|
|
||||||
tracked = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( access->target.val->GetProperties() & MutableVal::TRACKED )
|
|
||||||
tracked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( tracked )
|
|
||||||
notifiers.AccessPerformed(*access);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
ODesc desc;
|
|
||||||
access->Describe(&desc);
|
|
||||||
DBG_LOG(DBG_STATE, "operation: %s%s",
|
|
||||||
desc.Description(), replaying > 0 ? " (replay)" : "");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
delete access;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
NotifierRegistry notifiers;
|
|
||||||
|
|
||||||
void NotifierRegistry::Register(ID* id, NotifierRegistry::Notifier* notifier)
|
|
||||||
{
|
|
||||||
DBG_LOG(DBG_NOTIFIERS, "registering ID %s for notifier %s",
|
|
||||||
id->Name(), notifier->Name());
|
|
||||||
|
|
||||||
Attr* attr = new Attr(ATTR_TRACKED);
|
|
||||||
|
|
||||||
if ( id->Attrs() )
|
|
||||||
{
|
|
||||||
if ( ! id->Attrs()->FindAttr(ATTR_TRACKED) )
|
|
||||||
id->Attrs()->AddAttr(attr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
attr_list* a = new attr_list{attr};
|
|
||||||
id->SetAttrs(new Attributes(a, id->Type(), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
Unref(attr);
|
|
||||||
|
|
||||||
NotifierMap::iterator i = ids.find(id->Name());
|
|
||||||
|
|
||||||
if ( i != ids.end() )
|
|
||||||
i->second->insert(notifier);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NotifierSet* s = new NotifierSet;
|
|
||||||
s->insert(notifier);
|
|
||||||
ids.insert(NotifierMap::value_type(id->Name(), s));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierRegistry::Register(Val* val, NotifierRegistry::Notifier* notifier)
|
|
||||||
{
|
|
||||||
if ( val->IsMutableVal() )
|
|
||||||
Register(val->AsMutableVal()->UniqueID(), notifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierRegistry::Unregister(ID* id, NotifierRegistry::Notifier* notifier)
|
|
||||||
{
|
|
||||||
DBG_LOG(DBG_NOTIFIERS, "unregistering ID %s for notifier %s",
|
|
||||||
id->Name(), notifier->Name());
|
|
||||||
|
|
||||||
NotifierMap::iterator i = ids.find(id->Name());
|
|
||||||
|
|
||||||
if ( i == ids.end() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
Attr* attr = id->Attrs()->FindAttr(ATTR_TRACKED);
|
|
||||||
id->Attrs()->RemoveAttr(ATTR_TRACKED);
|
|
||||||
Unref(attr);
|
|
||||||
|
|
||||||
NotifierSet* s = i->second;
|
|
||||||
s->erase(notifier);
|
|
||||||
|
|
||||||
if ( s->size() == 0 )
|
|
||||||
{
|
|
||||||
delete s;
|
|
||||||
ids.erase(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unref(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierRegistry::Unregister(Val* val, NotifierRegistry::Notifier* notifier)
|
|
||||||
{
|
|
||||||
if ( val->IsMutableVal() )
|
|
||||||
Unregister(val->AsMutableVal()->UniqueID(), notifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierRegistry::AccessPerformed(const StateAccess& sa)
|
|
||||||
{
|
|
||||||
ID* id = sa.Target();
|
|
||||||
|
|
||||||
NotifierMap::iterator i = ids.find(id->Name());
|
|
||||||
|
|
||||||
if ( i == ids.end() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
DBG_LOG(DBG_NOTIFIERS, "modification to tracked ID %s", id->Name());
|
|
||||||
|
|
||||||
NotifierSet* s = i->second;
|
|
||||||
|
|
||||||
if ( id->IsInternalGlobal() )
|
|
||||||
for ( NotifierSet::iterator j = s->begin(); j != s->end(); j++ )
|
|
||||||
(*j)->Access(id->ID_Val(), sa);
|
|
||||||
else
|
|
||||||
for ( NotifierSet::iterator j = s->begin(); j != s->end(); j++ )
|
|
||||||
(*j)->Access(id, sa);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* NotifierRegistry::Notifier::Name() const
|
|
||||||
{
|
|
||||||
return fmt("%p", this);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,138 +0,0 @@
|
||||||
// A class describing a state-modyfing access to a Value or an ID.
|
|
||||||
|
|
||||||
#ifndef STATEACESSS_H
|
|
||||||
#define STATEACESSS_H
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class Val;
|
|
||||||
class ID;
|
|
||||||
class MutableVal;
|
|
||||||
class HashKey;
|
|
||||||
class ODesc;
|
|
||||||
class TableVal;
|
|
||||||
|
|
||||||
enum Opcode { // Op1 Op2 Op3 (Vals)
|
|
||||||
OP_NONE,
|
|
||||||
OP_ASSIGN, // new old
|
|
||||||
OP_ASSIGN_IDX, // idx new old
|
|
||||||
OP_ADD, // idx old
|
|
||||||
OP_INCR, // idx new old
|
|
||||||
OP_INCR_IDX, // idx new old
|
|
||||||
OP_DEL, // idx old
|
|
||||||
OP_PRINT, // args
|
|
||||||
OP_EXPIRE, // idx
|
|
||||||
OP_READ_IDX, // idx
|
|
||||||
};
|
|
||||||
|
|
||||||
class StateAccess {
|
|
||||||
public:
|
|
||||||
StateAccess(Opcode opcode, const ID* target, const Val* op1,
|
|
||||||
const Val* op2 = 0, const Val* op3 = 0);
|
|
||||||
StateAccess(Opcode opcode, const MutableVal* target, const Val* op1,
|
|
||||||
const Val* op2 = 0, const Val* op3 = 0);
|
|
||||||
|
|
||||||
// For tables, the idx operand may be given as an index HashKey.
|
|
||||||
// This is for efficiency. While we need to reconstruct the index
|
|
||||||
// if we are actually going to serialize the access, we can at
|
|
||||||
// least skip it if we don't.
|
|
||||||
StateAccess(Opcode opcode, const ID* target, const HashKey* op1,
|
|
||||||
const Val* op2 = 0, const Val* op3 = 0);
|
|
||||||
StateAccess(Opcode opcode, const MutableVal* target, const HashKey* op1,
|
|
||||||
const Val* op2 = 0, const Val* op3 = 0);
|
|
||||||
|
|
||||||
StateAccess(const StateAccess& sa);
|
|
||||||
|
|
||||||
virtual ~StateAccess();
|
|
||||||
|
|
||||||
// Replays this access in the our environment.
|
|
||||||
void Replay();
|
|
||||||
|
|
||||||
// Returns target ID which may be an internal one for unbound vals.
|
|
||||||
ID* Target() const;
|
|
||||||
|
|
||||||
void Describe(ODesc* d) const;
|
|
||||||
|
|
||||||
// Main entry point when StateAcesses are performed.
|
|
||||||
// For every state-changing operation, this has to be called.
|
|
||||||
static void Log(StateAccess* access);
|
|
||||||
|
|
||||||
// If we're going to make additional non-replaying accesses during a
|
|
||||||
// Replay(), we have to call these.
|
|
||||||
static void SuspendReplay() { --replaying; }
|
|
||||||
static void ResumeReplay() { ++replaying; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
StateAccess() { target.id = 0; op1.val = op2 = op3 = 0; }
|
|
||||||
void RefThem();
|
|
||||||
|
|
||||||
Opcode opcode;
|
|
||||||
union {
|
|
||||||
ID* id;
|
|
||||||
MutableVal* val;
|
|
||||||
} target;
|
|
||||||
|
|
||||||
union {
|
|
||||||
Val* val;
|
|
||||||
const HashKey* key;
|
|
||||||
} op1;
|
|
||||||
|
|
||||||
Val* op2;
|
|
||||||
Val* op3;
|
|
||||||
|
|
||||||
enum Type { TYPE_ID, TYPE_VAL, TYPE_MVAL, TYPE_KEY };
|
|
||||||
Type target_type;
|
|
||||||
Type op1_type;
|
|
||||||
bool delete_op1_key;
|
|
||||||
|
|
||||||
static int replaying;
|
|
||||||
};
|
|
||||||
|
|
||||||
// We provide a notifier framework to inform interested parties of
|
|
||||||
// modifications to selected global IDs/Vals. To get notified about a change,
|
|
||||||
// derive a class from Notifier and register the interesting IDs/Vals with
|
|
||||||
// the NotifierRegistry.
|
|
||||||
//
|
|
||||||
// Note: For containers (e.g., tables), notifications are only issued if the
|
|
||||||
// container itself is modified, *not* for changes to the values contained
|
|
||||||
// therein.
|
|
||||||
|
|
||||||
class NotifierRegistry {
|
|
||||||
public:
|
|
||||||
class Notifier {
|
|
||||||
public:
|
|
||||||
virtual ~Notifier() { }
|
|
||||||
|
|
||||||
// Called when a change is being performed. Note that when these
|
|
||||||
// methods are called, it is undefined whether the change has
|
|
||||||
// already been done or is just going to be performed soon.
|
|
||||||
virtual void Access(ID* id, const StateAccess& sa) = 0;
|
|
||||||
virtual void Access(Val* val, const StateAccess& sa) = 0;
|
|
||||||
virtual const char* Name() const; // for debugging
|
|
||||||
};
|
|
||||||
|
|
||||||
NotifierRegistry() { }
|
|
||||||
~NotifierRegistry() { }
|
|
||||||
|
|
||||||
// Inform the given notifier if ID/Val changes.
|
|
||||||
void Register(ID* id, Notifier* notifier);
|
|
||||||
void Register(Val* val, Notifier* notifier);
|
|
||||||
|
|
||||||
// Cancel notification for this ID/Val.
|
|
||||||
void Unregister(ID* id, Notifier* notifier);
|
|
||||||
void Unregister(Val* val, Notifier* notifier);
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class StateAccess;
|
|
||||||
void AccessPerformed(const StateAccess& sa);
|
|
||||||
|
|
||||||
typedef std::set<Notifier*> NotifierSet;
|
|
||||||
typedef std::map<std::string, NotifierSet*> NotifierMap;
|
|
||||||
NotifierMap ids;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern NotifierRegistry notifiers;
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -255,7 +255,7 @@ void ProfileLogger::Log()
|
||||||
while ( (id = globals->NextEntry(c)) )
|
while ( (id = globals->NextEntry(c)) )
|
||||||
// We don't show/count internal globals as they are always
|
// We don't show/count internal globals as they are always
|
||||||
// contained in some other global user-visible container.
|
// contained in some other global user-visible container.
|
||||||
if ( id->HasVal() && ! id->IsInternalGlobal() )
|
if ( id->HasVal() )
|
||||||
{
|
{
|
||||||
Val* v = id->ID_Val();
|
Val* v = id->ID_Val();
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ TraversalCode TriggerTraversalCallback::PreExpr(const Expr* expr)
|
||||||
trigger->Register(e->Id());
|
trigger->Register(e->Id());
|
||||||
|
|
||||||
Val* v = e->Id()->ID_Val();
|
Val* v = e->Id()->ID_Val();
|
||||||
if ( v && v->IsMutableVal() )
|
if ( v && v->Modifiable() )
|
||||||
trigger->Register(v);
|
trigger->Register(v);
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
@ -382,38 +382,35 @@ void Trigger::Timeout()
|
||||||
void Trigger::Register(ID* id)
|
void Trigger::Register(ID* id)
|
||||||
{
|
{
|
||||||
assert(! disabled);
|
assert(! disabled);
|
||||||
notifiers.Register(id, this);
|
notifier::registry.Register(id, this);
|
||||||
|
|
||||||
Ref(id);
|
Ref(id);
|
||||||
ids.insert(id);
|
objs.push_back({id, id});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trigger::Register(Val* val)
|
void Trigger::Register(Val* val)
|
||||||
{
|
{
|
||||||
|
if ( ! val->Modifiable() )
|
||||||
|
return;
|
||||||
|
|
||||||
assert(! disabled);
|
assert(! disabled);
|
||||||
notifiers.Register(val, this);
|
notifier::registry.Register(val->Modifiable(), this);
|
||||||
|
|
||||||
Ref(val);
|
Ref(val);
|
||||||
vals.insert(val);
|
objs.emplace_back(val, val->Modifiable());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trigger::UnregisterAll()
|
void Trigger::UnregisterAll()
|
||||||
{
|
{
|
||||||
loop_over_list(ids, i)
|
DBG_LOG(DBG_NOTIFIERS, "%s: unregistering all", Name());
|
||||||
|
|
||||||
|
for ( const auto& o : objs )
|
||||||
{
|
{
|
||||||
notifiers.Unregister(ids[i], this);
|
notifier::registry.Unregister(o.second, this);
|
||||||
Unref(ids[i]);
|
Unref(o.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
ids.clear();
|
objs.clear();
|
||||||
|
|
||||||
loop_over_list(vals, j)
|
|
||||||
{
|
|
||||||
notifiers.Unregister(vals[j], this);
|
|
||||||
Unref(vals[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
vals.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trigger::Attach(Trigger *trigger)
|
void Trigger::Attach(Trigger *trigger)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "StateAccess.h"
|
#include "Notifier.h"
|
||||||
#include "Traverse.h"
|
#include "Traverse.h"
|
||||||
|
|
||||||
// Triggers are the heart of "when" statements: expressions that when
|
// Triggers are the heart of "when" statements: expressions that when
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
class TriggerTimer;
|
class TriggerTimer;
|
||||||
class TriggerTraversalCallback;
|
class TriggerTraversalCallback;
|
||||||
|
|
||||||
class Trigger : public NotifierRegistry::Notifier, public BroObj {
|
class Trigger : public BroObj, public notifier::Receiver {
|
||||||
public:
|
public:
|
||||||
// Don't access Trigger objects; they take care of themselves after
|
// Don't access Trigger objects; they take care of themselves after
|
||||||
// instantiation. Note that if the condition is already true, the
|
// instantiation. Note that if the condition is already true, the
|
||||||
|
@ -61,12 +61,10 @@ public:
|
||||||
{ d->Add("<trigger>"); }
|
{ d->Add("<trigger>"); }
|
||||||
// Overidden from Notifier. We queue the trigger and evaluate it
|
// Overidden from Notifier. We queue the trigger and evaluate it
|
||||||
// later to avoid race conditions.
|
// later to avoid race conditions.
|
||||||
void Access(ID* id, const StateAccess& sa) override
|
void Modified(notifier::Modifiable* m) override
|
||||||
{ QueueTrigger(this); }
|
|
||||||
void Access(Val* val, const StateAccess& sa) override
|
|
||||||
{ QueueTrigger(this); }
|
{ QueueTrigger(this); }
|
||||||
|
|
||||||
const char* Name() const override;
|
const char* Name() const;
|
||||||
|
|
||||||
static void QueueTrigger(Trigger* trigger);
|
static void QueueTrigger(Trigger* trigger);
|
||||||
|
|
||||||
|
@ -104,8 +102,7 @@ private:
|
||||||
bool delayed; // true if a function call is currently being delayed
|
bool delayed; // true if a function call is currently being delayed
|
||||||
bool disabled;
|
bool disabled;
|
||||||
|
|
||||||
val_list vals;
|
std::vector<std::pair<BroObj *, notifier::Modifiable*>> objs;
|
||||||
id_list ids;
|
|
||||||
|
|
||||||
typedef map<const CallExpr*, Val*> ValCache;
|
typedef map<const CallExpr*, Val*> ValCache;
|
||||||
ValCache cache;
|
ValCache cache;
|
||||||
|
|
|
@ -695,10 +695,6 @@ bool is_atomic_type(const BroType* t);
|
||||||
// True if the given type tag corresponds to a function type.
|
// True if the given type tag corresponds to a function type.
|
||||||
#define IsFunc(t) (t == TYPE_FUNC)
|
#define IsFunc(t) (t == TYPE_FUNC)
|
||||||
|
|
||||||
// True if the given type tag corresponds to mutable type.
|
|
||||||
#define IsMutable(t) \
|
|
||||||
(t == TYPE_RECORD || t == TYPE_TABLE || t == TYPE_VECTOR)
|
|
||||||
|
|
||||||
// True if the given type type is a vector.
|
// True if the given type type is a vector.
|
||||||
#define IsVector(t) (t == TYPE_VECTOR)
|
#define IsVector(t) (t == TYPE_VECTOR)
|
||||||
|
|
||||||
|
|
401
src/Val.cc
401
src/Val.cc
|
@ -380,128 +380,6 @@ bool Val::WouldOverflow(const BroType* from_type, const BroType* to_type, const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MutableVal::~MutableVal()
|
|
||||||
{
|
|
||||||
for ( list<ID*>::iterator i = aliases.begin(); i != aliases.end(); ++i )
|
|
||||||
{
|
|
||||||
if ( global_scope() )
|
|
||||||
global_scope()->Remove((*i)->Name());
|
|
||||||
(*i)->ClearVal(); // just to make sure.
|
|
||||||
Unref((*i));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( id )
|
|
||||||
{
|
|
||||||
if ( global_scope() )
|
|
||||||
global_scope()->Remove(id->Name());
|
|
||||||
id->ClearVal(); // just to make sure.
|
|
||||||
Unref(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MutableVal::AddProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( (props | arg_props) == props )
|
|
||||||
// No change.
|
|
||||||
return false;
|
|
||||||
|
|
||||||
props |= arg_props;
|
|
||||||
|
|
||||||
if ( ! id )
|
|
||||||
Bind();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool MutableVal::RemoveProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( (props & ~arg_props) == props )
|
|
||||||
// No change.
|
|
||||||
return false;
|
|
||||||
|
|
||||||
props &= ~arg_props;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ID* MutableVal::Bind() const
|
|
||||||
{
|
|
||||||
static bool initialized = false;
|
|
||||||
|
|
||||||
assert(!id);
|
|
||||||
|
|
||||||
static unsigned int id_counter = 0;
|
|
||||||
static const int MAX_NAME_SIZE = 128;
|
|
||||||
static char name[MAX_NAME_SIZE];
|
|
||||||
static char* end_of_static_str = 0;
|
|
||||||
|
|
||||||
if ( ! initialized )
|
|
||||||
{
|
|
||||||
// Get local IP.
|
|
||||||
char host[MAXHOSTNAMELEN];
|
|
||||||
strcpy(host, "localhost");
|
|
||||||
gethostname(host, MAXHOSTNAMELEN);
|
|
||||||
host[MAXHOSTNAMELEN-1] = '\0';
|
|
||||||
#if 0
|
|
||||||
// We ignore errors.
|
|
||||||
struct hostent* ent = gethostbyname(host);
|
|
||||||
|
|
||||||
uint32 ip;
|
|
||||||
if ( ent && ent->h_addr_list[0] )
|
|
||||||
ip = *(uint32*) ent->h_addr_list[0];
|
|
||||||
else
|
|
||||||
ip = htonl(0x7f000001); // 127.0.0.1
|
|
||||||
|
|
||||||
safe_snprintf(name, MAX_NAME_SIZE, "#%s#%d#",
|
|
||||||
IPAddr(IPv4, &ip, IPAddr::Network)->AsString().c_str(),
|
|
||||||
getpid());
|
|
||||||
#else
|
|
||||||
safe_snprintf(name, MAX_NAME_SIZE, "#%s#%d#", host, getpid());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
end_of_static_str = name + strlen(name);
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
safe_snprintf(end_of_static_str, MAX_NAME_SIZE - (end_of_static_str - name),
|
|
||||||
"%u", ++id_counter);
|
|
||||||
name[MAX_NAME_SIZE-1] = '\0';
|
|
||||||
|
|
||||||
// DBG_LOG(DBG_STATE, "new unique ID %s", name);
|
|
||||||
|
|
||||||
id = new ID(name, SCOPE_GLOBAL, true);
|
|
||||||
id->SetType(const_cast<MutableVal*>(this)->Type()->Ref());
|
|
||||||
|
|
||||||
global_scope()->Insert(name, id);
|
|
||||||
|
|
||||||
id->SetVal(const_cast<MutableVal*>(this), OP_NONE, true);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MutableVal::TransferUniqueID(MutableVal* mv)
|
|
||||||
{
|
|
||||||
const char* new_name = mv->UniqueID()->Name();
|
|
||||||
|
|
||||||
if ( ! id )
|
|
||||||
Bind();
|
|
||||||
|
|
||||||
DBG_LOG(DBG_STATE, "transfering ID (new %s, old/alias %s)", new_name, id->Name());
|
|
||||||
|
|
||||||
// Keep old name as alias.
|
|
||||||
aliases.push_back(id);
|
|
||||||
|
|
||||||
id = new ID(new_name, SCOPE_GLOBAL, true);
|
|
||||||
id->SetType(const_cast<MutableVal*>(this)->Type()->Ref());
|
|
||||||
global_scope()->Insert(new_name, id);
|
|
||||||
id->SetVal(const_cast<MutableVal*>(this), OP_NONE, true);
|
|
||||||
|
|
||||||
Unref(mv->id);
|
|
||||||
mv->id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntervalVal::IntervalVal(double quantity, double units) :
|
IntervalVal::IntervalVal(double quantity, double units) :
|
||||||
Val(quantity * units, TYPE_INTERVAL)
|
Val(quantity * units, TYPE_INTERVAL)
|
||||||
{
|
{
|
||||||
|
@ -1056,7 +934,7 @@ static void table_entry_val_delete_func(void* val)
|
||||||
delete tv;
|
delete tv;
|
||||||
}
|
}
|
||||||
|
|
||||||
TableVal::TableVal(TableType* t, Attributes* a) : MutableVal(t)
|
TableVal::TableVal(TableType* t, Attributes* a) : Val(t)
|
||||||
{
|
{
|
||||||
Init(t);
|
Init(t);
|
||||||
SetAttrs(a);
|
SetAttrs(a);
|
||||||
|
@ -1175,7 +1053,7 @@ void TableVal::CheckExpireAttr(attr_tag at)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int TableVal::Assign(Val* index, Val* new_val, Opcode op)
|
int TableVal::Assign(Val* index, Val* new_val)
|
||||||
{
|
{
|
||||||
HashKey* k = ComputeHash(index);
|
HashKey* k = ComputeHash(index);
|
||||||
if ( ! k )
|
if ( ! k )
|
||||||
|
@ -1185,10 +1063,10 @@ int TableVal::Assign(Val* index, Val* new_val, Opcode op)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Assign(index, k, new_val, op);
|
return Assign(index, k, new_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TableVal::Assign(Val* index, HashKey* k, Val* new_val, Opcode op)
|
int TableVal::Assign(Val* index, HashKey* k, Val* new_val)
|
||||||
{
|
{
|
||||||
int is_set = table_type->IsSet();
|
int is_set = table_type->IsSet();
|
||||||
|
|
||||||
|
@ -1217,55 +1095,6 @@ int TableVal::Assign(Val* index, HashKey* k, Val* new_val, Opcode op)
|
||||||
subnets->Insert(index, new_entry_val);
|
subnets->Insert(index, new_entry_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( LoggingAccess() && op != OP_NONE )
|
|
||||||
{
|
|
||||||
Val* rec_index = 0;
|
|
||||||
if ( ! index )
|
|
||||||
index = rec_index = RecoverIndex(&k_copy);
|
|
||||||
|
|
||||||
if ( new_val )
|
|
||||||
{
|
|
||||||
// A table.
|
|
||||||
if ( new_val->IsMutableVal() )
|
|
||||||
new_val->AsMutableVal()->AddProperties(GetProperties());
|
|
||||||
|
|
||||||
bool unref_old_val = false;
|
|
||||||
Val* old_val = old_entry_val ?
|
|
||||||
old_entry_val->Value() : 0;
|
|
||||||
if ( op == OP_INCR && ! old_val )
|
|
||||||
// If it's an increment, somebody has already
|
|
||||||
// checked that the index is there. If it's
|
|
||||||
// not, that can only be due to using the
|
|
||||||
// default.
|
|
||||||
{
|
|
||||||
old_val = Default(index);
|
|
||||||
unref_old_val = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(op != OP_INCR || old_val);
|
|
||||||
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(
|
|
||||||
op == OP_INCR ?
|
|
||||||
OP_INCR_IDX : OP_ASSIGN_IDX,
|
|
||||||
this, index, new_val, old_val));
|
|
||||||
|
|
||||||
if ( unref_old_val )
|
|
||||||
Unref(old_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// A set.
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(OP_ADD, this,
|
|
||||||
index, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( rec_index )
|
|
||||||
Unref(rec_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep old expiration time if necessary.
|
// Keep old expiration time if necessary.
|
||||||
if ( old_entry_val && attrs && attrs->FindAttr(ATTR_EXPIRE_CREATE) )
|
if ( old_entry_val && attrs && attrs->FindAttr(ATTR_EXPIRE_CREATE) )
|
||||||
new_entry_val->SetExpireAccess(old_entry_val->ExpireAccessTime());
|
new_entry_val->SetExpireAccess(old_entry_val->ExpireAccessTime());
|
||||||
|
@ -1276,6 +1105,7 @@ int TableVal::Assign(Val* index, HashKey* k, Val* new_val, Opcode op)
|
||||||
delete old_entry_val;
|
delete old_entry_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Modified();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1318,15 +1148,13 @@ int TableVal::AddTo(Val* val, int is_first_init, bool propagate_ops) const
|
||||||
|
|
||||||
if ( type->IsSet() )
|
if ( type->IsSet() )
|
||||||
{
|
{
|
||||||
if ( ! t->Assign(v->Value(), k, 0,
|
if ( ! t->Assign(v->Value(), k, 0) )
|
||||||
propagate_ops ? OP_ASSIGN : OP_NONE) )
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
v->Ref();
|
v->Ref();
|
||||||
if ( ! t->Assign(0, k, v->Value(),
|
if ( ! t->Assign(0, k, v->Value()) )
|
||||||
propagate_ops ? OP_ASSIGN : OP_NONE) )
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1595,11 +1423,7 @@ Val* TableVal::Lookup(Val* index, bool use_default_val)
|
||||||
if ( v )
|
if ( v )
|
||||||
{
|
{
|
||||||
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
||||||
{
|
|
||||||
v->SetExpireAccess(network_time);
|
v->SetExpireAccess(network_time);
|
||||||
if ( LoggingAccess() && ExpirationEnabled() )
|
|
||||||
ReadOperation(index, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return v->Value() ? v->Value() : this;
|
return v->Value() ? v->Value() : this;
|
||||||
}
|
}
|
||||||
|
@ -1626,11 +1450,7 @@ Val* TableVal::Lookup(Val* index, bool use_default_val)
|
||||||
if ( v )
|
if ( v )
|
||||||
{
|
{
|
||||||
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
||||||
{
|
|
||||||
v->SetExpireAccess(network_time);
|
v->SetExpireAccess(network_time);
|
||||||
if ( LoggingAccess() && ExpirationEnabled() )
|
|
||||||
ReadOperation(index, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return v->Value() ? v->Value() : this;
|
return v->Value() ? v->Value() : this;
|
||||||
}
|
}
|
||||||
|
@ -1684,11 +1504,7 @@ TableVal* TableVal::LookupSubnetValues(const SubNetVal* search)
|
||||||
if ( entry )
|
if ( entry )
|
||||||
{
|
{
|
||||||
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
||||||
{
|
|
||||||
entry->SetExpireAccess(network_time);
|
entry->SetExpireAccess(network_time);
|
||||||
if ( LoggingAccess() && ExpirationEnabled() )
|
|
||||||
ReadOperation(s, entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Unref(s); // assign does not consume index
|
Unref(s); // assign does not consume index
|
||||||
|
@ -1718,8 +1534,6 @@ bool TableVal::UpdateTimestamp(Val* index)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
v->SetExpireAccess(network_time);
|
v->SetExpireAccess(network_time);
|
||||||
if ( LoggingAccess() && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
|
||||||
ReadOperation(index, v);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1738,25 +1552,10 @@ Val* TableVal::Delete(const Val* index)
|
||||||
if ( subnets && ! subnets->Remove(index) )
|
if ( subnets && ! subnets->Remove(index) )
|
||||||
reporter->InternalWarning("index not in prefix table");
|
reporter->InternalWarning("index not in prefix table");
|
||||||
|
|
||||||
if ( LoggingAccess() )
|
|
||||||
{
|
|
||||||
if ( v )
|
|
||||||
{
|
|
||||||
// A set.
|
|
||||||
Val* has_old_val = val_mgr->GetInt(1);
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(OP_DEL, this, index,
|
|
||||||
has_old_val));
|
|
||||||
Unref(has_old_val);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(OP_DEL, this, index, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
delete k;
|
delete k;
|
||||||
delete v;
|
delete v;
|
||||||
|
|
||||||
|
Modified();
|
||||||
return va;
|
return va;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1775,9 +1574,7 @@ Val* TableVal::Delete(const HashKey* k)
|
||||||
|
|
||||||
delete v;
|
delete v;
|
||||||
|
|
||||||
if ( LoggingAccess() )
|
Modified();
|
||||||
StateAccess::Log(new StateAccess(OP_DEL, this, k));
|
|
||||||
|
|
||||||
return va;
|
return va;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1944,7 +1741,7 @@ int TableVal::ExpandCompoundAndInit(val_list* vl, int k, Val* new_val)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TableVal::CheckAndAssign(Val* index, Val* new_val, Opcode op)
|
int TableVal::CheckAndAssign(Val* index, Val* new_val)
|
||||||
{
|
{
|
||||||
Val* v = 0;
|
Val* v = 0;
|
||||||
if ( subnets )
|
if ( subnets )
|
||||||
|
@ -1956,7 +1753,7 @@ int TableVal::CheckAndAssign(Val* index, Val* new_val, Opcode op)
|
||||||
if ( v )
|
if ( v )
|
||||||
index->Warn("multiple initializations for index");
|
index->Warn("multiple initializations for index");
|
||||||
|
|
||||||
return Assign(index, new_val, op);
|
return Assign(index, new_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TableVal::InitTimer(double delay)
|
void TableVal::InitTimer(double delay)
|
||||||
|
@ -1988,6 +1785,7 @@ void TableVal::DoExpire(double t)
|
||||||
HashKey* k = 0;
|
HashKey* k = 0;
|
||||||
TableEntryVal* v = 0;
|
TableEntryVal* v = 0;
|
||||||
TableEntryVal* v_saved = 0;
|
TableEntryVal* v_saved = 0;
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
for ( int i = 0; i < table_incremental_step &&
|
for ( int i = 0; i < table_incremental_step &&
|
||||||
(v = tbl->NextEntry(k, expire_cookie)); ++i )
|
(v = tbl->NextEntry(k, expire_cookie)); ++i )
|
||||||
|
@ -2040,18 +1838,18 @@ void TableVal::DoExpire(double t)
|
||||||
Unref(index);
|
Unref(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( LoggingAccess() )
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(OP_EXPIRE, this, k));
|
|
||||||
|
|
||||||
tbl->RemoveEntry(k);
|
tbl->RemoveEntry(k);
|
||||||
Unref(v->Value());
|
Unref(v->Value());
|
||||||
delete v;
|
delete v;
|
||||||
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete k;
|
delete k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( modified )
|
||||||
|
Modified();
|
||||||
|
|
||||||
if ( ! v )
|
if ( ! v )
|
||||||
{
|
{
|
||||||
expire_cookie = 0;
|
expire_cookie = 0;
|
||||||
|
@ -2149,26 +1947,6 @@ double TableVal::CallExpireFunc(Val* idx)
|
||||||
return secs;
|
return secs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TableVal::ReadOperation(Val* index, TableEntryVal* v)
|
|
||||||
{
|
|
||||||
double timeout = GetExpireTime();
|
|
||||||
|
|
||||||
if ( timeout < 0 )
|
|
||||||
// Skip in case of unset/invalid expiration value. If it's an
|
|
||||||
// error, it has been reported already.
|
|
||||||
return;
|
|
||||||
|
|
||||||
// In theory we need to only propagate one update per &read_expire
|
|
||||||
// interval to prevent peers from expiring intervals. To account for
|
|
||||||
// practical issues such as latency, we send one update every half
|
|
||||||
// &read_expire.
|
|
||||||
if ( network_time - v->LastReadUpdate() > timeout / 2 )
|
|
||||||
{
|
|
||||||
StateAccess::Log(new StateAccess(OP_READ_IDX, this, index));
|
|
||||||
v->SetLastReadUpdate(network_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Val* TableVal::DoClone(CloneState* state)
|
Val* TableVal::DoClone(CloneState* state)
|
||||||
{
|
{
|
||||||
auto tv = new TableVal(table_type);
|
auto tv = new TableVal(table_type);
|
||||||
|
@ -2219,48 +1997,6 @@ Val* TableVal::DoClone(CloneState* state)
|
||||||
return tv;
|
return tv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TableVal::AddProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::AddProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( Type()->IsSet() || ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// For a large table, this could get expensive. So, let's hope
|
|
||||||
// that nobody creates such a table *before* making it persistent
|
|
||||||
// (for example by inserting it into another table).
|
|
||||||
TableEntryVal* v;
|
|
||||||
PDict(TableEntryVal)* tbl = val.table_val;
|
|
||||||
IterCookie* c = tbl->InitForIteration();
|
|
||||||
while ( (v = tbl->NextEntry(c)) )
|
|
||||||
if ( v->Value()->IsMutableVal() )
|
|
||||||
v->Value()->AsMutableVal()->AddProperties(RecursiveProps(arg_props));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TableVal::RemoveProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::RemoveProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( Type()->IsSet() || ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// For a large table, this could get expensive. So, let's hope
|
|
||||||
// that nobody creates such a table *before* making it persistent
|
|
||||||
// (for example by inserting it into another table).
|
|
||||||
TableEntryVal* v;
|
|
||||||
PDict(TableEntryVal)* tbl = val.table_val;
|
|
||||||
IterCookie* c = tbl->InitForIteration();
|
|
||||||
while ( (v = tbl->NextEntry(c)) )
|
|
||||||
if ( v->Value()->IsMutableVal() )
|
|
||||||
v->Value()->AsMutableVal()->RemoveProperties(RecursiveProps(arg_props));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int TableVal::MemoryAllocation() const
|
unsigned int TableVal::MemoryAllocation() const
|
||||||
{
|
{
|
||||||
unsigned int size = 0;
|
unsigned int size = 0;
|
||||||
|
@ -2282,7 +2018,7 @@ unsigned int TableVal::MemoryAllocation() const
|
||||||
|
|
||||||
vector<RecordVal*> RecordVal::parse_time_records;
|
vector<RecordVal*> RecordVal::parse_time_records;
|
||||||
|
|
||||||
RecordVal::RecordVal(RecordType* t, bool init_fields) : MutableVal(t)
|
RecordVal::RecordVal(RecordType* t, bool init_fields) : Val(t)
|
||||||
{
|
{
|
||||||
origin = 0;
|
origin = 0;
|
||||||
int n = t->NumFields();
|
int n = t->NumFields();
|
||||||
|
@ -2343,24 +2079,11 @@ RecordVal::~RecordVal()
|
||||||
delete_vals(AsNonConstRecord());
|
delete_vals(AsNonConstRecord());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordVal::Assign(int field, Val* new_val, Opcode op)
|
void RecordVal::Assign(int field, Val* new_val)
|
||||||
{
|
{
|
||||||
Val* old_val = AsNonConstRecord()->replace(field, new_val);
|
Val* old_val = AsNonConstRecord()->replace(field, new_val);
|
||||||
|
|
||||||
if ( LoggingAccess() && op != OP_NONE )
|
|
||||||
{
|
|
||||||
if ( new_val && new_val->IsMutableVal() )
|
|
||||||
new_val->AsMutableVal()->AddProperties(GetProperties());
|
|
||||||
|
|
||||||
StringVal* index = new StringVal(Type()->AsRecordType()->FieldName(field));
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(
|
|
||||||
op == OP_INCR ? OP_INCR_IDX : OP_ASSIGN_IDX,
|
|
||||||
this, index, new_val, old_val));
|
|
||||||
Unref(index); // The logging may keep a cached copy.
|
|
||||||
}
|
|
||||||
|
|
||||||
Unref(old_val);
|
Unref(old_val);
|
||||||
|
Modified();
|
||||||
}
|
}
|
||||||
|
|
||||||
Val* RecordVal::Lookup(int field) const
|
Val* RecordVal::Lookup(int field) const
|
||||||
|
@ -2570,41 +2293,6 @@ Val* RecordVal::DoClone(CloneState* state)
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecordVal::AddProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::AddProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
loop_over_list(*val.val_list_val, i)
|
|
||||||
{
|
|
||||||
Val* v = (*val.val_list_val)[i];
|
|
||||||
if ( v && v->IsMutableVal() )
|
|
||||||
v->AsMutableVal()->AddProperties(RecursiveProps(arg_props));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool RecordVal::RemoveProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::RemoveProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
loop_over_list(*val.val_list_val, i)
|
|
||||||
{
|
|
||||||
Val* v = (*val.val_list_val)[i];
|
|
||||||
if ( v && v->IsMutableVal() )
|
|
||||||
v->AsMutableVal()->RemoveProperties(RecursiveProps(arg_props));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int RecordVal::MemoryAllocation() const
|
unsigned int RecordVal::MemoryAllocation() const
|
||||||
{
|
{
|
||||||
unsigned int size = 0;
|
unsigned int size = 0;
|
||||||
|
@ -2637,7 +2325,7 @@ Val* EnumVal::DoClone(CloneState* state)
|
||||||
return Ref();
|
return Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorVal::VectorVal(VectorType* t) : MutableVal(t)
|
VectorVal::VectorVal(VectorType* t) : Val(t)
|
||||||
{
|
{
|
||||||
vector_type = t->Ref()->AsVectorType();
|
vector_type = t->Ref()->AsVectorType();
|
||||||
val.vector_val = new vector<Val*>();
|
val.vector_val = new vector<Val*>();
|
||||||
|
@ -2653,7 +2341,7 @@ VectorVal::~VectorVal()
|
||||||
delete val.vector_val;
|
delete val.vector_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VectorVal::Assign(unsigned int index, Val* element, Opcode op)
|
bool VectorVal::Assign(unsigned int index, Val* element)
|
||||||
{
|
{
|
||||||
if ( element &&
|
if ( element &&
|
||||||
! same_type(element->Type(), vector_type->YieldType(), 0) )
|
! same_type(element->Type(), vector_type->YieldType(), 0) )
|
||||||
|
@ -2669,19 +2357,6 @@ bool VectorVal::Assign(unsigned int index, Val* element, Opcode op)
|
||||||
else
|
else
|
||||||
val.vector_val->resize(index + 1);
|
val.vector_val->resize(index + 1);
|
||||||
|
|
||||||
if ( LoggingAccess() && op != OP_NONE )
|
|
||||||
{
|
|
||||||
if ( element->IsMutableVal() )
|
|
||||||
element->AsMutableVal()->AddProperties(GetProperties());
|
|
||||||
|
|
||||||
Val* ival = val_mgr->GetCount(index);
|
|
||||||
|
|
||||||
StateAccess::Log(new StateAccess(op == OP_INCR ?
|
|
||||||
OP_INCR_IDX : OP_ASSIGN_IDX,
|
|
||||||
this, ival, element, val_at_index));
|
|
||||||
Unref(ival);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unref(val_at_index);
|
Unref(val_at_index);
|
||||||
|
|
||||||
// Note: we do *not* Ref() the element, if any, at this point.
|
// Note: we do *not* Ref() the element, if any, at this point.
|
||||||
|
@ -2689,6 +2364,7 @@ bool VectorVal::Assign(unsigned int index, Val* element, Opcode op)
|
||||||
// to do it similarly.
|
// to do it similarly.
|
||||||
(*val.vector_val)[index] = element;
|
(*val.vector_val)[index] = element;
|
||||||
|
|
||||||
|
Modified();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2725,6 +2401,7 @@ bool VectorVal::Insert(unsigned int index, Val* element)
|
||||||
// to do it similarly.
|
// to do it similarly.
|
||||||
val.vector_val->insert(it, element);
|
val.vector_val->insert(it, element);
|
||||||
|
|
||||||
|
Modified();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2738,6 +2415,7 @@ bool VectorVal::Remove(unsigned int index)
|
||||||
val.vector_val->erase(it);
|
val.vector_val->erase(it);
|
||||||
Unref(val_at_index);
|
Unref(val_at_index);
|
||||||
|
|
||||||
|
Modified();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2790,37 +2468,6 @@ unsigned int VectorVal::ResizeAtLeast(unsigned int new_num_elements)
|
||||||
return Resize(new_num_elements);
|
return Resize(new_num_elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VectorVal::AddProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::AddProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for ( unsigned int i = 0; i < val.vector_val->size(); ++i )
|
|
||||||
if ( (*val.vector_val)[i]->IsMutableVal() )
|
|
||||||
(*val.vector_val)[i]->AsMutableVal()->AddProperties(RecursiveProps(arg_props));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VectorVal::RemoveProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::RemoveProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for ( unsigned int i = 0; i < val.vector_val->size(); ++i )
|
|
||||||
if ( (*val.vector_val)[i]->IsMutableVal() )
|
|
||||||
(*val.vector_val)[i]->AsMutableVal()->RemoveProperties(RecursiveProps(arg_props));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Val* VectorVal::DoClone(CloneState* state)
|
Val* VectorVal::DoClone(CloneState* state)
|
||||||
{
|
{
|
||||||
auto vv = new VectorVal(vector_type);
|
auto vv = new VectorVal(vector_type);
|
||||||
|
|
144
src/Val.h
144
src/Val.h
|
@ -17,7 +17,7 @@
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
#include "ID.h"
|
#include "ID.h"
|
||||||
#include "Scope.h"
|
#include "Scope.h"
|
||||||
#include "StateAccess.h"
|
#include "Notifier.h"
|
||||||
#include "IPAddr.h"
|
#include "IPAddr.h"
|
||||||
#include "DebugLogger.h"
|
#include "DebugLogger.h"
|
||||||
|
|
||||||
|
@ -48,7 +48,6 @@ class RecordVal;
|
||||||
class ListVal;
|
class ListVal;
|
||||||
class StringVal;
|
class StringVal;
|
||||||
class EnumVal;
|
class EnumVal;
|
||||||
class MutableVal;
|
|
||||||
class OpaqueVal;
|
class OpaqueVal;
|
||||||
|
|
||||||
class StateAccess;
|
class StateAccess;
|
||||||
|
@ -324,28 +323,13 @@ public:
|
||||||
CONST_CONVERTER(TYPE_VECTOR, VectorVal*, AsVectorVal)
|
CONST_CONVERTER(TYPE_VECTOR, VectorVal*, AsVectorVal)
|
||||||
CONST_CONVERTER(TYPE_OPAQUE, OpaqueVal*, AsOpaqueVal)
|
CONST_CONVERTER(TYPE_OPAQUE, OpaqueVal*, AsOpaqueVal)
|
||||||
|
|
||||||
bool IsMutableVal() const
|
|
||||||
{
|
|
||||||
return IsMutable(type->Tag());
|
|
||||||
}
|
|
||||||
|
|
||||||
const MutableVal* AsMutableVal() const
|
|
||||||
{
|
|
||||||
if ( ! IsMutableVal() )
|
|
||||||
BadTag("Val::AsMutableVal", type_name(type->Tag()));
|
|
||||||
return (MutableVal*) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
MutableVal* AsMutableVal()
|
|
||||||
{
|
|
||||||
if ( ! IsMutableVal() )
|
|
||||||
BadTag("Val::AsMutableVal", type_name(type->Tag()));
|
|
||||||
return (MutableVal*) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Describe(ODesc* d) const override;
|
void Describe(ODesc* d) const override;
|
||||||
virtual void DescribeReST(ODesc* d) const;
|
virtual void DescribeReST(ODesc* d) const;
|
||||||
|
|
||||||
|
// To be overridden by mutable derived class to enable change
|
||||||
|
// notification.
|
||||||
|
virtual notifier::Modifiable* Modifiable() { return 0; }
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// For debugging, we keep a reference to the global ID to which a
|
// For debugging, we keep a reference to the global ID to which a
|
||||||
// value has been bound *last*.
|
// value has been bound *last*.
|
||||||
|
@ -514,69 +498,6 @@ private:
|
||||||
|
|
||||||
extern ValManager* val_mgr;
|
extern ValManager* val_mgr;
|
||||||
|
|
||||||
class MutableVal : public Val {
|
|
||||||
public:
|
|
||||||
// Each MutableVal gets a globally unique ID that can be used to
|
|
||||||
// reference it no matter if it's directly bound to any user-visible
|
|
||||||
// ID. This ID is inserted into the global namespace.
|
|
||||||
ID* UniqueID() const { return id ? id : Bind(); }
|
|
||||||
|
|
||||||
// Returns true if we've already generated a unique ID.
|
|
||||||
bool HasUniqueID() const { return id; }
|
|
||||||
|
|
||||||
// Transfers the unique ID of the given value to this value. We keep our
|
|
||||||
// old ID as an alias.
|
|
||||||
void TransferUniqueID(MutableVal* mv);
|
|
||||||
|
|
||||||
// MutableVals can have properties (let's refrain from calling them
|
|
||||||
// attributes!). Most properties are recursive. If a derived object
|
|
||||||
// can contain MutableVals itself, the object has to override
|
|
||||||
// {Add,Remove}Properties(). RecursiveProp(state) masks out all non-
|
|
||||||
// recursive properties. If this is non-null, an overriden method must
|
|
||||||
// call itself with RecursiveProp(state) as argument for all contained
|
|
||||||
// values. (In any case, don't forget to call the parent's method.)
|
|
||||||
typedef char Properties;
|
|
||||||
|
|
||||||
// Tracked by NotifierRegistry, not recursive.
|
|
||||||
static const int TRACKED = 0x04;
|
|
||||||
|
|
||||||
int RecursiveProps(int prop) const { return prop & ~TRACKED; }
|
|
||||||
|
|
||||||
Properties GetProperties() const { return props; }
|
|
||||||
virtual bool AddProperties(Properties state);
|
|
||||||
virtual bool RemoveProperties(Properties state);
|
|
||||||
|
|
||||||
// Whether StateAccess:LogAccess needs to be called.
|
|
||||||
bool LoggingAccess() const
|
|
||||||
{
|
|
||||||
#ifndef DEBUG
|
|
||||||
return props & TRACKED;
|
|
||||||
#else
|
|
||||||
return debug_logger.IsVerbose() ||
|
|
||||||
(props & TRACKED);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit MutableVal(BroType* t) : Val(t)
|
|
||||||
{ props = 0; id = 0; }
|
|
||||||
MutableVal() { props = 0; id = 0; }
|
|
||||||
~MutableVal() override;
|
|
||||||
|
|
||||||
friend class ID;
|
|
||||||
friend class Val;
|
|
||||||
|
|
||||||
void SetID(ID* arg_id) { Unref(id); id = arg_id; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ID* Bind() const;
|
|
||||||
|
|
||||||
mutable ID* id;
|
|
||||||
list<ID*> aliases;
|
|
||||||
Properties props;
|
|
||||||
uint64 last_modified;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define Microseconds 1e-6
|
#define Microseconds 1e-6
|
||||||
#define Milliseconds 1e-3
|
#define Milliseconds 1e-3
|
||||||
#define Seconds 1.0
|
#define Seconds 1.0
|
||||||
|
@ -797,7 +718,7 @@ public:
|
||||||
{
|
{
|
||||||
val = v;
|
val = v;
|
||||||
last_access_time = network_time;
|
last_access_time = network_time;
|
||||||
expire_access_time = last_read_update =
|
expire_access_time =
|
||||||
int(network_time - bro_start_network_time);
|
int(network_time - bro_start_network_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,7 +727,6 @@ public:
|
||||||
auto rval = new TableEntryVal(val ? val->Clone(state) : nullptr);
|
auto rval = new TableEntryVal(val ? val->Clone(state) : nullptr);
|
||||||
rval->last_access_time = last_access_time;
|
rval->last_access_time = last_access_time;
|
||||||
rval->expire_access_time = expire_access_time;
|
rval->expire_access_time = expire_access_time;
|
||||||
rval->last_read_update = last_read_update;
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,24 +742,16 @@ public:
|
||||||
void SetExpireAccess(double time)
|
void SetExpireAccess(double time)
|
||||||
{ expire_access_time = int(time - bro_start_network_time); }
|
{ expire_access_time = int(time - bro_start_network_time); }
|
||||||
|
|
||||||
// Returns/sets time of when we propagated the last OP_READ_IDX
|
|
||||||
// for this item.
|
|
||||||
double LastReadUpdate() const
|
|
||||||
{ return bro_start_network_time + last_read_update; }
|
|
||||||
void SetLastReadUpdate(double time)
|
|
||||||
{ last_read_update = int(time - bro_start_network_time); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class TableVal;
|
friend class TableVal;
|
||||||
|
|
||||||
Val* val;
|
Val* val;
|
||||||
double last_access_time;
|
double last_access_time;
|
||||||
|
|
||||||
// The next two entries store seconds since Bro's start. We use
|
// The next entry stores seconds since Bro's start. We use ints here
|
||||||
// ints here to save a few bytes, as we do not need a high resolution
|
// to save a few bytes, as we do not need a high resolution for these
|
||||||
// for these anyway.
|
// anyway.
|
||||||
int expire_access_time;
|
int expire_access_time;
|
||||||
int last_read_update;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TableValTimer : public Timer {
|
class TableValTimer : public Timer {
|
||||||
|
@ -856,7 +768,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
class CompositeHash;
|
class CompositeHash;
|
||||||
class TableVal : public MutableVal {
|
class TableVal : public Val, public notifier::Modifiable {
|
||||||
public:
|
public:
|
||||||
explicit TableVal(TableType* t, Attributes* attrs = 0);
|
explicit TableVal(TableType* t, Attributes* attrs = 0);
|
||||||
~TableVal() override;
|
~TableVal() override;
|
||||||
|
@ -866,8 +778,8 @@ public:
|
||||||
// version takes a HashKey and Unref()'s it when done. If we're a
|
// version takes a HashKey and Unref()'s it when done. If we're a
|
||||||
// set, new_val has to be nil. If we aren't a set, index may be nil
|
// set, new_val has to be nil. If we aren't a set, index may be nil
|
||||||
// in the second version.
|
// in the second version.
|
||||||
int Assign(Val* index, Val* new_val, Opcode op = OP_ASSIGN);
|
int Assign(Val* index, Val* new_val);
|
||||||
int Assign(Val* index, HashKey* k, Val* new_val, Opcode op = OP_ASSIGN);
|
int Assign(Val* index, HashKey* k, Val* new_val);
|
||||||
|
|
||||||
Val* SizeVal() const override { return val_mgr->GetCount(Size()); }
|
Val* SizeVal() const override { return val_mgr->GetCount(Size()); }
|
||||||
|
|
||||||
|
@ -969,19 +881,17 @@ public:
|
||||||
HashKey* ComputeHash(const Val* index) const
|
HashKey* ComputeHash(const Val* index) const
|
||||||
{ return table_hash->ComputeHash(index, 1); }
|
{ return table_hash->ComputeHash(index, 1); }
|
||||||
|
|
||||||
|
notifier::Modifiable* Modifiable() override { return this; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Val;
|
friend class Val;
|
||||||
friend class StateAccess;
|
|
||||||
TableVal() {}
|
TableVal() {}
|
||||||
|
|
||||||
void Init(TableType* t);
|
void Init(TableType* t);
|
||||||
|
|
||||||
void CheckExpireAttr(attr_tag at);
|
void CheckExpireAttr(attr_tag at);
|
||||||
int ExpandCompoundAndInit(val_list* vl, int k, Val* new_val);
|
int ExpandCompoundAndInit(val_list* vl, int k, Val* new_val);
|
||||||
int CheckAndAssign(Val* index, Val* new_val, Opcode op = OP_ASSIGN);
|
int CheckAndAssign(Val* index, Val* new_val);
|
||||||
|
|
||||||
bool AddProperties(Properties arg_state) override;
|
|
||||||
bool RemoveProperties(Properties arg_state) override;
|
|
||||||
|
|
||||||
// Calculates default value for index. Returns 0 if none.
|
// Calculates default value for index. Returns 0 if none.
|
||||||
Val* Default(Val* index);
|
Val* Default(Val* index);
|
||||||
|
@ -998,9 +908,6 @@ protected:
|
||||||
// takes ownership of the reference.
|
// takes ownership of the reference.
|
||||||
double CallExpireFunc(Val *idx);
|
double CallExpireFunc(Val *idx);
|
||||||
|
|
||||||
// Propagates a read operation if necessary.
|
|
||||||
void ReadOperation(Val* index, TableEntryVal *v);
|
|
||||||
|
|
||||||
Val* DoClone(CloneState* state) override;
|
Val* DoClone(CloneState* state) override;
|
||||||
|
|
||||||
TableType* table_type;
|
TableType* table_type;
|
||||||
|
@ -1014,7 +921,7 @@ protected:
|
||||||
Val* def_val;
|
Val* def_val;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RecordVal : public MutableVal {
|
class RecordVal : public Val, public notifier::Modifiable {
|
||||||
public:
|
public:
|
||||||
explicit RecordVal(RecordType* t, bool init_fields = true);
|
explicit RecordVal(RecordType* t, bool init_fields = true);
|
||||||
~RecordVal() override;
|
~RecordVal() override;
|
||||||
|
@ -1022,7 +929,7 @@ public:
|
||||||
Val* SizeVal() const override
|
Val* SizeVal() const override
|
||||||
{ return val_mgr->GetCount(Type()->AsRecordType()->NumFields()); }
|
{ return val_mgr->GetCount(Type()->AsRecordType()->NumFields()); }
|
||||||
|
|
||||||
void Assign(int field, Val* new_val, Opcode op = OP_ASSIGN);
|
void Assign(int field, Val* new_val);
|
||||||
Val* Lookup(int field) const; // Does not Ref() value.
|
Val* Lookup(int field) const; // Does not Ref() value.
|
||||||
Val* LookupWithDefault(int field) const; // Does Ref() value.
|
Val* LookupWithDefault(int field) const; // Does Ref() value.
|
||||||
|
|
||||||
|
@ -1061,6 +968,8 @@ public:
|
||||||
unsigned int MemoryAllocation() const override;
|
unsigned int MemoryAllocation() const override;
|
||||||
void DescribeReST(ODesc* d) const override;
|
void DescribeReST(ODesc* d) const override;
|
||||||
|
|
||||||
|
notifier::Modifiable* Modifiable() override { return this; }
|
||||||
|
|
||||||
// Extend the underlying arrays of record instances created during
|
// Extend the underlying arrays of record instances created during
|
||||||
// parsing to match the number of fields in the record type (they may
|
// parsing to match the number of fields in the record type (they may
|
||||||
// mismatch as a result of parse-time record type redefinitions.
|
// mismatch as a result of parse-time record type redefinitions.
|
||||||
|
@ -1070,9 +979,6 @@ protected:
|
||||||
friend class Val;
|
friend class Val;
|
||||||
RecordVal() {}
|
RecordVal() {}
|
||||||
|
|
||||||
bool AddProperties(Properties arg_state) override;
|
|
||||||
bool RemoveProperties(Properties arg_state) override;
|
|
||||||
|
|
||||||
Val* DoClone(CloneState* state) override;
|
Val* DoClone(CloneState* state) override;
|
||||||
|
|
||||||
RecordType* record_type;
|
RecordType* record_type;
|
||||||
|
@ -1108,7 +1014,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class VectorVal : public MutableVal {
|
class VectorVal : public Val, public notifier::Modifiable {
|
||||||
public:
|
public:
|
||||||
explicit VectorVal(VectorType* t);
|
explicit VectorVal(VectorType* t);
|
||||||
~VectorVal() override;
|
~VectorVal() override;
|
||||||
|
@ -1122,11 +1028,11 @@ public:
|
||||||
// Note: does NOT Ref() the element! Remember to do so unless
|
// Note: does NOT Ref() the element! Remember to do so unless
|
||||||
// the element was just created and thus has refcount 1.
|
// the element was just created and thus has refcount 1.
|
||||||
//
|
//
|
||||||
bool Assign(unsigned int index, Val* element, Opcode op = OP_ASSIGN);
|
bool Assign(unsigned int index, Val* element);
|
||||||
bool Assign(Val* index, Val* element, Opcode op = OP_ASSIGN)
|
bool Assign(Val* index, Val* element)
|
||||||
{
|
{
|
||||||
return Assign(index->AsListVal()->Index(0)->CoerceToUnsigned(),
|
return Assign(index->AsListVal()->Index(0)->CoerceToUnsigned(),
|
||||||
element, op);
|
element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assigns the value to how_many locations starting at index.
|
// Assigns the value to how_many locations starting at index.
|
||||||
|
@ -1156,6 +1062,8 @@ public:
|
||||||
// Won't shrink size.
|
// Won't shrink size.
|
||||||
unsigned int ResizeAtLeast(unsigned int new_num_elements);
|
unsigned int ResizeAtLeast(unsigned int new_num_elements);
|
||||||
|
|
||||||
|
notifier::Modifiable* Modifiable() override { return this; }
|
||||||
|
|
||||||
// Insert an element at a specific position into the underlying vector.
|
// Insert an element at a specific position into the underlying vector.
|
||||||
bool Insert(unsigned int index, Val* element);
|
bool Insert(unsigned int index, Val* element);
|
||||||
|
|
||||||
|
@ -1166,8 +1074,6 @@ protected:
|
||||||
friend class Val;
|
friend class Val;
|
||||||
VectorVal() { }
|
VectorVal() { }
|
||||||
|
|
||||||
bool AddProperties(Properties arg_state) override;
|
|
||||||
bool RemoveProperties(Properties arg_state) override;
|
|
||||||
void ValDescribe(ODesc* d) const override;
|
void ValDescribe(ODesc* d) const override;
|
||||||
Val* DoClone(CloneState* state) override;
|
Val* DoClone(CloneState* state) override;
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ Config::Config(ReaderFrontend *frontend) : ReaderBackend(frontend)
|
||||||
|
|
||||||
while ( auto id = globals->NextEntry(c) )
|
while ( auto id = globals->NextEntry(c) )
|
||||||
{
|
{
|
||||||
if ( id->IsInternalGlobal() || ! id->IsOption() )
|
if ( ! id->IsOption() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ( id->Type()->Tag() == TYPE_RECORD ||
|
if ( id->Type()->Tag() == TYPE_RECORD ||
|
||||||
|
|
16
src/zeek.bif
16
src/zeek.bif
|
@ -1835,7 +1835,7 @@ function global_sizes%(%): var_sizes
|
||||||
|
|
||||||
ID* id;
|
ID* id;
|
||||||
while ( (id = globals->NextEntry(c)) )
|
while ( (id = globals->NextEntry(c)) )
|
||||||
if ( id->HasVal() && ! id->IsInternalGlobal() )
|
if ( id->HasVal() )
|
||||||
{
|
{
|
||||||
Val* id_name = new StringVal(id->Name());
|
Val* id_name = new StringVal(id->Name());
|
||||||
Val* id_size = val_mgr->GetCount(id->ID_Val()->MemoryAllocation());
|
Val* id_size = val_mgr->GetCount(id->ID_Val()->MemoryAllocation());
|
||||||
|
@ -1859,24 +1859,10 @@ function global_ids%(%): id_table
|
||||||
TableVal* ids = new TableVal(id_table);
|
TableVal* ids = new TableVal(id_table);
|
||||||
PDict(ID)* globals = global_scope()->Vars();
|
PDict(ID)* globals = global_scope()->Vars();
|
||||||
IterCookie* c = globals->InitForIteration();
|
IterCookie* c = globals->InitForIteration();
|
||||||
#ifdef DEBUG
|
|
||||||
/**
|
|
||||||
* Explanation time: c needs to be a robust cookie when one is in debug mode,
|
|
||||||
* otherwise the Zeek process will crash in ~80% of cases when -B all is specified.
|
|
||||||
* The reason for this are the RecordVals that we create. RecordVal::Assign triggers
|
|
||||||
* a StateAccess::Log, which in turn (only in debug mode) triggers StateAccess::Describe,
|
|
||||||
* which creates a UniqueID for the variable, which triggers an insert into global_scope.
|
|
||||||
* Which invalidates the iteration cookie if it is not robust.
|
|
||||||
**/
|
|
||||||
globals->MakeRobustCookie(c);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ID* id;
|
ID* id;
|
||||||
while ( (id = globals->NextEntry(c)) )
|
while ( (id = globals->NextEntry(c)) )
|
||||||
{
|
{
|
||||||
if ( id->IsInternalGlobal() )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
RecordVal* rec = new RecordVal(script_id);
|
RecordVal* rec = new RecordVal(script_id);
|
||||||
rec->Assign(0, new StringVal(type_name(id->Type()->Tag())));
|
rec->Assign(0, new StringVal(type_name(id->Type()->Tag())));
|
||||||
rec->Assign(1, val_mgr->GetBool(id->IsExport()));
|
rec->Assign(1, val_mgr->GetBool(id->IsExport()));
|
||||||
|
|
4
testing/btest/Baseline/language.when-on-globals/out
Normal file
4
testing/btest/Baseline/language.when-on-globals/out
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
"j" in x3[20]$x, expected timeout
|
||||||
|
15 in x2, T
|
||||||
|
x1 != 42, T
|
||||||
|
x2[10], T
|
71
testing/btest/language/when-on-globals.zeek
Normal file
71
testing/btest/language/when-on-globals.zeek
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# @TEST-EXEC: zeek -b -r $TRACES/wikipedia.trace %INPUT | sort >out
|
||||||
|
# @TEST-EXEC: btest-diff out
|
||||||
|
|
||||||
|
redef exit_only_after_terminate = T;
|
||||||
|
|
||||||
|
type X: record {
|
||||||
|
s: string;
|
||||||
|
x: set[string] &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
global x1 = 42;
|
||||||
|
global x2: table[count] of X;
|
||||||
|
global x3: table[count] of X;
|
||||||
|
|
||||||
|
event quit()
|
||||||
|
{
|
||||||
|
terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
event zeek_init()
|
||||||
|
{
|
||||||
|
x2[10] = [$s="foo"];
|
||||||
|
x3[20] = [$s="bar", $x=set("i")];
|
||||||
|
|
||||||
|
when ( x1 != 42 )
|
||||||
|
{
|
||||||
|
print "x1 != 42", x1 != 42;
|
||||||
|
}
|
||||||
|
timeout 1sec
|
||||||
|
{
|
||||||
|
print "unexpected timeout (1)";
|
||||||
|
}
|
||||||
|
|
||||||
|
when ( 15 in x2 )
|
||||||
|
{
|
||||||
|
print "15 in x2", 10 in x2;
|
||||||
|
}
|
||||||
|
timeout 1sec
|
||||||
|
{
|
||||||
|
print "unexpected timeout (2)";
|
||||||
|
}
|
||||||
|
|
||||||
|
when ( x2[10]$s == "bar" )
|
||||||
|
{
|
||||||
|
print "x2[10]", x2[10]$s == "bar";
|
||||||
|
}
|
||||||
|
timeout 1sec
|
||||||
|
{
|
||||||
|
print "unexpected timeout (3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
when ( "j" in x3[20]$x )
|
||||||
|
{
|
||||||
|
print "unexpected trigger";
|
||||||
|
}
|
||||||
|
timeout 1sec
|
||||||
|
{
|
||||||
|
print "\"j\" in x3[20]$x, expected timeout";
|
||||||
|
}
|
||||||
|
|
||||||
|
x1 = 100;
|
||||||
|
x2[15] = [$s="xyz"];
|
||||||
|
x2[10]$s = "bar";
|
||||||
|
|
||||||
|
# This will *NOT* trigger then when-condition because we're modifying
|
||||||
|
# an inner value that's not directly tracked.
|
||||||
|
add x3[20]$x["j"];
|
||||||
|
|
||||||
|
schedule 2secs { quit() };
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue