mirror of
https://github.com/zeek/zeek.git
synced 2025-10-08 01:28:20 +00:00
520 lines
11 KiB
C++
520 lines
11 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "config.h"
|
|
|
|
#include "Var.h"
|
|
#include "Func.h"
|
|
#include "Stmt.h"
|
|
#include "Scope.h"
|
|
#include "Serializer.h"
|
|
#include "RemoteSerializer.h"
|
|
#include "EventRegistry.h"
|
|
|
|
extern int generate_documentation;
|
|
|
|
static Val* init_val(Expr* init, const BroType* t, Val* aggr)
|
|
{
|
|
return init->InitVal(t, aggr);
|
|
}
|
|
|
|
static void make_var(ID* id, BroType* t, init_class c, Expr* init,
|
|
attr_list* attr, decl_type dt, int do_init)
|
|
{
|
|
if ( id->Type() )
|
|
{
|
|
if ( id->IsRedefinable() || (! init && attr) )
|
|
{
|
|
BroObj* redef_obj = init ? (BroObj*) init : (BroObj*) t;
|
|
if ( dt != VAR_REDEF )
|
|
id->Warn("redefinition requires \"redef\"", redef_obj, 1);
|
|
}
|
|
|
|
else if ( dt != VAR_REDEF || init || ! attr )
|
|
{
|
|
id->Error("already defined", init);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( dt == VAR_REDEF )
|
|
{
|
|
if ( ! id->Type() )
|
|
{
|
|
id->Error("\"redef\" used but not previously defined");
|
|
return;
|
|
}
|
|
|
|
if ( ! t )
|
|
t = id->Type();
|
|
}
|
|
|
|
if ( id->Type() && id->Type()->Tag() != TYPE_ERROR )
|
|
{
|
|
if ( dt != VAR_REDEF &&
|
|
(! init || ! do_init || (! t && ! (t = init_type(init)))) )
|
|
{
|
|
id->Error("already defined", init);
|
|
return;
|
|
}
|
|
|
|
// Allow redeclaration in order to initialize.
|
|
if ( ! same_type(t, id->Type()) )
|
|
{
|
|
id->Error("redefinition changes type", init);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( init && optimize )
|
|
init = init->Simplify(SIMPLIFY_GENERAL);
|
|
|
|
if ( t && t->IsSet() )
|
|
{ // Check for set with explicit elements.
|
|
SetType* st = t->AsTableType()->AsSetType();
|
|
ListExpr* elements = st->SetElements();
|
|
|
|
if ( elements )
|
|
{
|
|
if ( init )
|
|
{
|
|
id->Error("double initialization", init);
|
|
return;
|
|
}
|
|
|
|
Ref(elements);
|
|
init = elements;
|
|
}
|
|
}
|
|
|
|
if ( ! t )
|
|
{ // Take type from initialization.
|
|
if ( ! init )
|
|
{
|
|
id->Error("no type given");
|
|
return;
|
|
}
|
|
|
|
t = init_type(init);
|
|
if ( ! t )
|
|
{
|
|
id->SetType(error_type());
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
Ref(t);
|
|
|
|
id->SetType(t);
|
|
|
|
if ( attr )
|
|
id->AddAttrs(new Attributes(attr, t, false));
|
|
|
|
if ( id->FindAttr(ATTR_PERSISTENT) || id->FindAttr(ATTR_SYNCHRONIZED) )
|
|
{
|
|
if ( dt == VAR_CONST )
|
|
{
|
|
id->Error("&persistant/synchronized with constant");
|
|
return;
|
|
}
|
|
|
|
if ( ! id->IsGlobal() )
|
|
{
|
|
id->Error("&persistant/synchronized with non-global");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( do_init )
|
|
{
|
|
if ( (c == INIT_EXTRA && id->FindAttr(ATTR_ADD_FUNC)) ||
|
|
(c == INIT_REMOVE && id->FindAttr(ATTR_DEL_FUNC)) )
|
|
// Just apply the function.
|
|
id->SetVal(init, c);
|
|
|
|
else if ( dt != VAR_REDEF || init || ! attr )
|
|
{
|
|
Val* aggr;
|
|
if ( t->Tag() == TYPE_RECORD )
|
|
aggr = new RecordVal(t->AsRecordType());
|
|
|
|
else if ( t->Tag() == TYPE_TABLE )
|
|
aggr = new TableVal(t->AsTableType(), id->Attrs());
|
|
|
|
else if ( t->Tag() == TYPE_VECTOR )
|
|
aggr = new VectorVal(t->AsVectorType());
|
|
|
|
else
|
|
aggr = 0;
|
|
|
|
Val* v = 0;
|
|
if ( init )
|
|
{
|
|
v = init_val(init, t, aggr);
|
|
if ( ! v )
|
|
return;
|
|
}
|
|
|
|
if ( aggr )
|
|
id->SetVal(aggr, c);
|
|
else if ( v )
|
|
id->SetVal(v, c);
|
|
}
|
|
}
|
|
|
|
if ( dt == VAR_CONST )
|
|
{
|
|
if ( ! init && ! id->IsRedefinable() )
|
|
id->Error("const variable must be initialized");
|
|
|
|
id->SetConst();
|
|
}
|
|
|
|
id->UpdateValAttrs();
|
|
|
|
if ( t && t->Tag() == TYPE_FUNC && t->AsFuncType()->IsEvent() )
|
|
{
|
|
// For events, add a function value (without any body) here so that
|
|
// we can later access the ID even if no implementations have been
|
|
// defined.
|
|
Func* f = new BroFunc(id, 0, 0, 0, 0);
|
|
id->SetVal(new Val(f));
|
|
id->SetConst();
|
|
}
|
|
}
|
|
|
|
|
|
void add_global(ID* id, BroType* t, init_class c, Expr* init,
|
|
attr_list* attr, decl_type dt)
|
|
{
|
|
make_var(id, t, c, init, attr, dt, 1);
|
|
}
|
|
|
|
Stmt* add_local(ID* id, BroType* t, init_class c, Expr* init,
|
|
attr_list* attr, decl_type dt)
|
|
{
|
|
make_var(id, t, c, init, attr, dt, 0);
|
|
|
|
if ( init )
|
|
{
|
|
if ( c != INIT_FULL )
|
|
id->Error("can't use += / -= for initializations of local variables");
|
|
|
|
Ref(id);
|
|
|
|
Stmt* stmt =
|
|
new ExprStmt(new AssignExpr(new NameExpr(id), init, 0, 0,
|
|
id->Attrs() ? id->Attrs()->Attrs() : 0 ));
|
|
stmt->SetLocationInfo(init->GetLocationInfo());
|
|
|
|
return stmt;
|
|
}
|
|
|
|
else
|
|
{
|
|
if ( t->Tag() == TYPE_RECORD || t->Tag() == TYPE_TABLE ||
|
|
t->Tag() == TYPE_VECTOR )
|
|
current_scope()->AddInit(id);
|
|
|
|
return new NullStmt;
|
|
}
|
|
}
|
|
|
|
extern Expr* add_and_assign_local(ID* id, Expr* init, Val* val)
|
|
{
|
|
make_var(id, 0, INIT_FULL, init, 0, VAR_REGULAR, 0);
|
|
Ref(id);
|
|
return new AssignExpr(new NameExpr(id), init, 0, val);
|
|
}
|
|
|
|
void add_type(ID* id, BroType* t, attr_list* attr, int /* is_event */)
|
|
{
|
|
BroType* tnew = t;
|
|
|
|
// In "documentation mode", we'd like to to be able to associate
|
|
// an identifier name with a declared type. Dealing with declared
|
|
// types that are "aliases" to a builtin type requires that the BroType
|
|
// is cloned before setting the identifier name that resolves to it.
|
|
// And still this is not enough to document cases where the declared type
|
|
// is an alias for another declared type -- but that's not a natural/common
|
|
// practice. If documenting that corner case is desired, one way
|
|
// is to add an ID* to class ID that tracks aliases and set it here if
|
|
// t->GetTypeID() is true.
|
|
if ( generate_documentation )
|
|
{
|
|
switch ( t->Tag() ) {
|
|
// Only "shallow" copy types that may contain records because
|
|
// we want to be able to see additions to the original record type's
|
|
// list of fields
|
|
case TYPE_RECORD:
|
|
tnew = new RecordType(t->AsRecordType()->Types());
|
|
break;
|
|
case TYPE_TABLE:
|
|
tnew = new TableType(t->AsTableType()->Indices(),
|
|
t->AsTableType()->YieldType());
|
|
break;
|
|
case TYPE_VECTOR:
|
|
tnew = new VectorType(t->AsVectorType()->YieldType());
|
|
break;
|
|
case TYPE_FUNC:
|
|
tnew = new FuncType(t->AsFuncType()->Args(),
|
|
t->AsFuncType()->YieldType(),
|
|
t->AsFuncType()->IsEvent());
|
|
break;
|
|
default:
|
|
SerializationFormat* form = new BinarySerializationFormat();
|
|
form->StartWrite();
|
|
CloneSerializer ss(form);
|
|
SerialInfo sinfo(&ss);
|
|
sinfo.cache = false;
|
|
|
|
t->Serialize(&sinfo);
|
|
char* data;
|
|
uint32 len = form->EndWrite(&data);
|
|
form->StartRead(data, len);
|
|
|
|
UnserialInfo uinfo(&ss);
|
|
uinfo.cache = false;
|
|
tnew = t->Unserialize(&uinfo);
|
|
|
|
delete [] data;
|
|
}
|
|
|
|
tnew->SetTypeID(copy_string(id->Name()));
|
|
}
|
|
|
|
id->SetType(tnew);
|
|
id->MakeType();
|
|
|
|
if ( attr )
|
|
id->SetAttrs(new Attributes(attr, tnew, false));
|
|
}
|
|
|
|
void begin_func(ID* id, const char* module_name, function_flavor flavor,
|
|
int is_redef, FuncType* t)
|
|
{
|
|
if ( flavor == FUNC_FLAVOR_EVENT )
|
|
{
|
|
const BroType* yt = t->YieldType();
|
|
|
|
if ( yt && yt->Tag() != TYPE_VOID )
|
|
id->Error("event cannot yield a value", t);
|
|
|
|
t->ClearYieldType();
|
|
}
|
|
|
|
if ( id->Type() )
|
|
{
|
|
if ( ! same_type(id->Type(), t) )
|
|
id->Type()->Error("incompatible types", t);
|
|
}
|
|
|
|
else if ( is_redef )
|
|
id->Error("redef of not-previously-declared value");
|
|
|
|
if ( id->HasVal() )
|
|
{
|
|
int id_is_event = id->ID_Val()->AsFunc()->IsEvent();
|
|
|
|
if ( id_is_event != (flavor == FUNC_FLAVOR_EVENT) )
|
|
id->Error("inconsistency between event and function", t);
|
|
if ( id_is_event )
|
|
{
|
|
if ( is_redef )
|
|
// Clear out value so it will be replaced.
|
|
id->SetVal(0);
|
|
}
|
|
else
|
|
{
|
|
if ( ! id->IsRedefinable() )
|
|
id->Error("already defined");
|
|
}
|
|
}
|
|
else
|
|
id->SetType(t);
|
|
|
|
push_scope(id);
|
|
|
|
RecordType* args = t->Args();
|
|
int num_args = args->NumFields();
|
|
for ( int i = 0; i < num_args; ++i )
|
|
{
|
|
TypeDecl* arg_i = args->FieldDecl(i);
|
|
ID* arg_id = lookup_ID(arg_i->id, module_name);
|
|
|
|
if ( arg_id && ! arg_id->IsGlobal() )
|
|
arg_id->Error("argument name used twice");
|
|
|
|
arg_id = install_ID(copy_string(arg_i->id), module_name,
|
|
false, false);
|
|
arg_id->SetType(arg_i->type->Ref());
|
|
}
|
|
}
|
|
|
|
void end_func(Stmt* body, attr_list* attrs)
|
|
{
|
|
int frame_size = current_scope()->Length();
|
|
id_list* inits = current_scope()->GetInits();
|
|
|
|
Scope* scope = pop_scope();
|
|
ID* id = scope->ScopeID();
|
|
|
|
int priority = 0;
|
|
if ( attrs )
|
|
{
|
|
loop_over_list(*attrs, i)
|
|
{
|
|
Attr* a = (*attrs)[i];
|
|
if ( a->Tag() != ATTR_PRIORITY )
|
|
{
|
|
a->Error("illegal attribute for function body");
|
|
continue;
|
|
}
|
|
|
|
Val* v = a->AttrExpr()->Eval(0);
|
|
if ( ! v )
|
|
{
|
|
a->Error("cannot evaluate attribute expression");
|
|
continue;
|
|
}
|
|
|
|
if ( ! IsIntegral(v->Type()->Tag()) )
|
|
{
|
|
a->Error("expression is not of integral type");
|
|
continue;
|
|
}
|
|
|
|
priority = v->InternalInt();
|
|
}
|
|
}
|
|
|
|
if ( id->HasVal() )
|
|
id->ID_Val()->AsFunc()->AddBody(body, inits, frame_size, priority);
|
|
else
|
|
{
|
|
Func* f = new BroFunc(id, body, inits, frame_size, priority);
|
|
id->SetVal(new Val(f));
|
|
id->SetConst();
|
|
}
|
|
|
|
id->ID_Val()->AsFunc()->SetScope(scope);
|
|
}
|
|
|
|
Val* internal_val(const char* name)
|
|
{
|
|
ID* id = lookup_ID(name, GLOBAL_MODULE_NAME);
|
|
if ( ! id )
|
|
reporter->InternalError("internal variable %s missing", name);
|
|
|
|
return id->ID_Val();
|
|
}
|
|
|
|
Val* internal_const_val(const char* name)
|
|
{
|
|
ID* id = lookup_ID(name, GLOBAL_MODULE_NAME);
|
|
if ( ! id )
|
|
reporter->InternalError("internal variable %s missing", name);
|
|
|
|
if ( ! id->IsConst() )
|
|
reporter->InternalError("internal variable %s is not constant", name);
|
|
|
|
return id->ID_Val();
|
|
}
|
|
|
|
Val* opt_internal_val(const char* name)
|
|
{
|
|
ID* id = lookup_ID(name, GLOBAL_MODULE_NAME);
|
|
return id ? id->ID_Val() : 0;
|
|
}
|
|
|
|
double opt_internal_double(const char* name)
|
|
{
|
|
Val* v = opt_internal_val(name);
|
|
return v ? v->InternalDouble() : 0.0;
|
|
}
|
|
|
|
bro_int_t opt_internal_int(const char* name)
|
|
{
|
|
Val* v = opt_internal_val(name);
|
|
return v ? v->InternalInt() : 0;
|
|
}
|
|
|
|
bro_uint_t opt_internal_unsigned(const char* name)
|
|
{
|
|
Val* v = opt_internal_val(name);
|
|
return v ? v->InternalUnsigned() : 0;
|
|
}
|
|
|
|
StringVal* opt_internal_string(const char* name)
|
|
{
|
|
Val* v = opt_internal_val(name);
|
|
return v ? v->AsStringVal() : 0;
|
|
}
|
|
|
|
TableVal* opt_internal_table(const char* name)
|
|
{
|
|
Val* v = opt_internal_val(name);
|
|
return v ? v->AsTableVal() : 0;
|
|
}
|
|
|
|
ListVal* internal_list_val(const char* name)
|
|
{
|
|
ID* id = lookup_ID(name, GLOBAL_MODULE_NAME);
|
|
if ( ! id )
|
|
return 0;
|
|
|
|
Val* v = id->ID_Val();
|
|
if ( v )
|
|
{
|
|
if ( v->Type()->Tag() == TYPE_LIST )
|
|
return (ListVal*) v;
|
|
|
|
else if ( v->Type()->IsSet() )
|
|
{
|
|
TableVal* tv = v->AsTableVal();
|
|
ListVal* lv = tv->ConvertToPureList();
|
|
return lv;
|
|
}
|
|
|
|
else
|
|
reporter->InternalError("internal variable %s is not a list", name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
BroType* internal_type(const char* name)
|
|
{
|
|
ID* id = lookup_ID(name, GLOBAL_MODULE_NAME);
|
|
if ( ! id )
|
|
reporter->InternalError("internal type %s missing", name);
|
|
|
|
return id->Type();
|
|
}
|
|
|
|
Func* internal_func(const char* name)
|
|
{
|
|
Val* v = internal_val(name);
|
|
if ( v )
|
|
return v->AsFunc();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
EventHandlerPtr internal_handler(const char* name)
|
|
{
|
|
// If there already is an entry in the registry, we have a
|
|
// local handler on the script layer.
|
|
EventHandler* h = event_registry->Lookup(name);
|
|
if ( h )
|
|
{
|
|
h->SetUsed();
|
|
return h;
|
|
}
|
|
|
|
h = new EventHandler(name);
|
|
event_registry->Register(h);
|
|
|
|
h->SetUsed();
|
|
|
|
return h;
|
|
}
|