zeek/src/Type.cc
Robin Sommer 2233521de7 Merge remote-tracking branch 'origin/topic/johanna/netcontrol'
BIT-1550 #merged

* origin/topic/johanna/netcontrol: (72 commits)
  Update baselines and news
  Move prefixtable back to all IPv6 internal handling.
  NetControl: Add functions to search for rules affecting IPs/subnets
  Add check_subnet bif that allows exact membership test for subnet tables.
  Rewrite internal handling of rules.
  Add bif that allows searching for all matching subnets in table.
  Add signaling of succesful initialization of plugins to NetControl.
  Add rule hooks to the acld plugin.
  Add new logfiles for shunting and drops to netcontrol
  Extend NetControl logging and fix bugs.
  Update OpenFlow API and events.
  small acld plugin fix
  Revert "introduce &weaken attribute"
  Fix crash when printing type of recursive structures.
  Testcase for crash when a record contains a function referencing a record.
  Rename Pacf to NetControl
  fix acld plugin to use address instead of subnet (and add functions for conversion)
  implement quarantine
  miscelaneous missing bits and pieces
  Acld implementation for Pacf - Bro side.
  ...
2016-03-11 14:29:23 -08:00

2474 lines
47 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include "bro-config.h"
#include "Type.h"
#include "Attr.h"
#include "Expr.h"
#include "Scope.h"
#include "Serializer.h"
#include "Reporter.h"
#include "broxygen/Manager.h"
#include "broxygen/utils.h"
#include <string>
#include <list>
#include <map>
BroType::TypeAliasMap BroType::type_aliases;
// Note: This function must be thread-safe.
const char* type_name(TypeTag t)
{
static const char* type_names[int(NUM_TYPES)] = {
"void", // 0
"bool", // 1
"int", // 2
"count", // 3
"counter", // 4
"double", // 5
"time", // 6
"interval", // 7
"string", // 8
"pattern", // 9
"enum", // 10
"timer", // 11
"port", // 12
"addr", // 13
"subnet", // 14
"any", // 15
"table", // 16
"union", // 17
"record", // 18
"types", // 19
"func", // 20
"file", // 21
"vector", // 22
"opaque", // 23
"type", // 24
"error", // 25
};
if ( int(t) >= NUM_TYPES )
return "type_name(): not a type tag";
return type_names[int(t)];
}
BroType::BroType(TypeTag t, bool arg_base_type)
{
tag = t;
is_network_order = 0;
base_type = arg_base_type;
switch ( tag ) {
case TYPE_VOID:
internal_tag = TYPE_INTERNAL_VOID;
break;
case TYPE_BOOL:
case TYPE_INT:
case TYPE_ENUM:
internal_tag = TYPE_INTERNAL_INT;
break;
case TYPE_COUNT:
case TYPE_COUNTER:
internal_tag = TYPE_INTERNAL_UNSIGNED;
break;
case TYPE_PORT:
internal_tag = TYPE_INTERNAL_UNSIGNED;
is_network_order = 1;
break;
case TYPE_DOUBLE:
case TYPE_TIME:
case TYPE_INTERVAL:
internal_tag = TYPE_INTERNAL_DOUBLE;
break;
case TYPE_STRING:
internal_tag = TYPE_INTERNAL_STRING;
break;
case TYPE_ADDR:
internal_tag = TYPE_INTERNAL_ADDR;
break;
case TYPE_SUBNET:
internal_tag = TYPE_INTERNAL_SUBNET;
break;
case TYPE_PATTERN:
case TYPE_TIMER:
case TYPE_ANY:
case TYPE_TABLE:
case TYPE_UNION:
case TYPE_RECORD:
case TYPE_LIST:
case TYPE_FUNC:
case TYPE_FILE:
case TYPE_OPAQUE:
case TYPE_VECTOR:
case TYPE_TYPE:
internal_tag = TYPE_INTERNAL_OTHER;
break;
case TYPE_ERROR:
internal_tag = TYPE_INTERNAL_ERROR;
break;
}
}
BroType* BroType::Clone() const
{
SerializationFormat* form = new BinarySerializationFormat();
form->StartWrite();
CloneSerializer ss(form);
SerialInfo sinfo(&ss);
sinfo.cache = false;
this->Serialize(&sinfo);
char* data;
uint32 len = form->EndWrite(&data);
form->StartRead(data, len);
UnserialInfo uinfo(&ss);
uinfo.cache = false;
BroType* rval = this->Unserialize(&uinfo, false);
assert(rval != this);
free(data);
return rval;
}
int BroType::MatchesIndex(ListExpr*& index) const
{
if ( Tag() == TYPE_STRING )
{
if ( index->Exprs().length() != 1 && index->Exprs().length() != 2 )
return DOES_NOT_MATCH_INDEX;
if ( check_and_promote_exprs_to_type(index, ::base_type(TYPE_INT)) )
return MATCHES_INDEX_SCALAR;
}
return DOES_NOT_MATCH_INDEX;
}
BroType* BroType::YieldType()
{
return 0;
}
int BroType::HasField(const char* /* field */) const
{
return 0;
}
BroType* BroType::FieldType(const char* /* field */) const
{
return 0;
}
void BroType::Describe(ODesc* d) const
{
if ( d->IsBinary() )
d->Add(int(Tag()));
else
{
TypeTag t = Tag();
if ( IsSet() )
d->Add("set");
else
d->Add(type_name(t));
}
}
void BroType::DescribeReST(ODesc* d, bool roles_only) const
{
d->Add(fmt(":bro:type:`%s`", type_name(Tag())));
}
void BroType::SetError()
{
tag = TYPE_ERROR;
}
unsigned int BroType::MemoryAllocation() const
{
return padded_sizeof(*this);
}
bool BroType::Serialize(SerialInfo* info) const
{
// We always send full types (see below).
if ( ! SERIALIZE(true) )
return false;
bool ret = SerialObj::Serialize(info);
return ret;
}
BroType* BroType::Unserialize(UnserialInfo* info, bool use_existing)
{
// To avoid external Broccoli clients needing to always send full type
// objects, we allow them to give us only the name of a type. To
// differentiate between the two cases, we exchange a flag first.
bool full_type = true;;
if ( ! UNSERIALIZE(&full_type) )
return 0;
if ( ! full_type )
{
const char* name;
if ( ! UNSERIALIZE_STR(&name, 0) )
return 0;
ID* id = global_scope()->Lookup(name);
if ( ! id )
{
info->s->Error(fmt("unknown type %s", name));
return 0;
}
BroType* t = id->AsType();
if ( ! t )
{
info->s->Error(fmt("%s is not a type", name));
return 0;
}
return t->Ref();
}
BroType* t = (BroType*) SerialObj::Unserialize(info, SER_BRO_TYPE);
if ( ! t || ! use_existing )
return t;
if ( ! t->name.empty() )
{
// Avoid creating a new type if it's known by name.
// Also avoids loss of base type name alias (from condition below).
ID* id = global_scope()->Lookup(t->name.c_str());
BroType* t2 = id ? id->AsType() : 0;
if ( t2 )
{
Unref(t);
return t2->Ref();
}
}
if ( t->base_type )
{
BroType* t2 = ::base_type(TypeTag(t->tag));
Unref(t);
assert(t2);
return t2;
}
assert(t);
return t;
}
IMPLEMENT_SERIAL(BroType, SER_BRO_TYPE)
bool BroType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_BRO_TYPE, BroObj);
info->s->WriteOpenTag("Type");
if ( ! (SERIALIZE(char(tag)) && SERIALIZE(char(internal_tag))) )
return false;
if ( ! (SERIALIZE(is_network_order) && SERIALIZE(base_type)) )
return false;
SERIALIZE_STR(name.c_str(), name.size());
info->s->WriteCloseTag("Type");
return true;
}
bool BroType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroObj);
char c1, c2;
if ( ! (UNSERIALIZE(&c1) && UNSERIALIZE(&c2) ) )
return 0;
tag = (TypeTag) c1;
internal_tag = (InternalTypeTag) c2;
if ( ! (UNSERIALIZE(&is_network_order) && UNSERIALIZE(&base_type)) )
return 0;
const char* n;
if ( ! UNSERIALIZE_STR(&n, 0) )
return false;
name = n;
delete [] n;
return true;
}
TypeList::~TypeList()
{
loop_over_list(types, i)
Unref(types[i]);
Unref(pure_type);
}
int TypeList::AllMatch(const BroType* t, int is_init) const
{
loop_over_list(types, i)
if ( ! same_type(types[i], t, is_init) )
return 0;
return 1;
}
void TypeList::Append(BroType* t)
{
if ( pure_type && ! same_type(t, pure_type) )
reporter->InternalError("pure type-list violation");
types.append(t);
}
void TypeList::AppendEvenIfNotPure(BroType* t)
{
if ( pure_type && ! same_type(t, pure_type) )
{
Unref(pure_type);
pure_type = 0;
}
types.append(t);
}
void TypeList::Describe(ODesc* d) const
{
if ( d->IsReadable() )
d->AddSP("list of");
else
{
d->Add(int(Tag()));
d->Add(IsPure());
if ( IsPure() )
pure_type->Describe(d);
d->Add(types.length());
}
if ( IsPure() )
pure_type->Describe(d);
else
{
loop_over_list(types, i)
{
if ( i > 0 && ! d->IsBinary() )
d->Add(",");
types[i]->Describe(d);
}
}
}
IMPLEMENT_SERIAL(TypeList, SER_TYPE_LIST);
bool TypeList::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_TYPE_LIST, BroType);
SERIALIZE_OPTIONAL(pure_type);
if ( ! SERIALIZE(types.length()) )
return false;
loop_over_list(types, j)
{
if ( ! types[j]->Serialize(info) )
return false;
}
return true;
}
bool TypeList::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
UNSERIALIZE_OPTIONAL(pure_type, BroType::Unserialize(info));
int len;
if ( ! UNSERIALIZE(&len) )
return false;
while ( len-- )
{
BroType* t = BroType::Unserialize(info);
if ( ! t )
return false;
types.append(t);
}
return true;
}
IndexType::~IndexType()
{
Unref(indices);
Unref(yield_type);
}
int IndexType::MatchesIndex(ListExpr*& index) const
{
// If we have a type indexed by subnets, addresses are ok.
const type_list* types = indices->Types();
const expr_list& exprs = index->Exprs();
if ( types->length() == 1 && (*types)[0]->Tag() == TYPE_SUBNET &&
exprs.length() == 1 && exprs[0]->Type()->Tag() == TYPE_ADDR )
return MATCHES_INDEX_SCALAR;
return check_and_promote_exprs(index, Indices()) ?
MATCHES_INDEX_SCALAR : DOES_NOT_MATCH_INDEX;
}
BroType* IndexType::YieldType()
{
return yield_type;
}
const BroType* IndexType::YieldType() const
{
return yield_type;
}
void IndexType::Describe(ODesc* d) const
{
BroType::Describe(d);
if ( ! d->IsBinary() )
d->Add("[");
loop_over_list(*IndexTypes(), i)
{
if ( ! d->IsBinary() && i > 0 )
d->Add(",");
(*IndexTypes())[i]->Describe(d);
}
if ( ! d->IsBinary() )
d->Add("]");
if ( yield_type )
{
if ( ! d->IsBinary() )
d->Add(" of ");
yield_type->Describe(d);
}
}
void IndexType::DescribeReST(ODesc* d, bool roles_only) const
{
d->Add(":bro:type:`");
if ( IsSet() )
d->Add("set");
else
d->Add(type_name(Tag()));
d->Add("` ");
d->Add("[");
loop_over_list(*IndexTypes(), i)
{
if ( i > 0 )
d->Add(", ");
const BroType* t = (*IndexTypes())[i];
if ( ! t->GetName().empty() )
{
d->Add(":bro:type:`");
d->Add(t->GetName());
d->Add("`");
}
else
t->DescribeReST(d, roles_only);
}
d->Add("]");
if ( yield_type )
{
d->Add(" of ");
if ( ! yield_type->GetName().empty() )
{
d->Add(":bro:type:`");
d->Add(yield_type->GetName());
d->Add("`");
}
else
yield_type->DescribeReST(d, roles_only);
}
}
bool IndexType::IsSubNetIndex() const
{
const type_list* types = indices->Types();
if ( types->length() == 1 && (*types)[0]->Tag() == TYPE_SUBNET )
return true;
return false;
}
IMPLEMENT_SERIAL(IndexType, SER_INDEX_TYPE);
bool IndexType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_INDEX_TYPE, BroType);
SERIALIZE_OPTIONAL(yield_type);
return indices->Serialize(info);
}
bool IndexType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
UNSERIALIZE_OPTIONAL(yield_type, BroType::Unserialize(info));
indices = (TypeList*) BroType::Unserialize(info, TYPE_LIST);
return indices != 0;
}
TableType::TableType(TypeList* ind, BroType* yield)
: IndexType(TYPE_TABLE, ind, yield)
{
if ( ! indices )
return;
type_list* tl = indices->Types();
loop_over_list(*tl, i)
{
BroType* tli = (*tl)[i];
InternalTypeTag t = tli->InternalType();
if ( t == TYPE_INTERNAL_ERROR )
break;
// Allow functions, since they can be compared
// for Func* pointer equality.
if ( t == TYPE_INTERNAL_OTHER && tli->Tag() != TYPE_FUNC &&
tli->Tag() != TYPE_RECORD )
{
tli->Error("bad index type");
SetError();
break;
}
}
}
bool TableType::IsUnspecifiedTable() const
{
// Unspecified types have an empty list of indices.
return indices->Types()->length() == 0;
}
TypeList* TableType::ExpandRecordIndex(RecordType* rt) const
{
TypeList* tl = new TypeList();
int n = rt->NumFields();
for ( int i = 0; i < n; ++i )
{
TypeDecl* td = rt->FieldDecl(i);
tl->Append(td->type->Ref());
}
return tl;
}
SetType::SetType(TypeList* ind, ListExpr* arg_elements) : TableType(ind, 0)
{
elements = arg_elements;
if ( elements )
{
if ( indices )
{ // We already have a type.
if ( ! check_and_promote_exprs(elements, indices) )
SetError();
}
else
{
TypeList* tl_type = elements->Type()->AsTypeList();
type_list* tl = tl_type->Types();
if ( tl->length() < 1 )
{
Error("no type given for set");
SetError();
}
else if ( tl->length() == 1 )
{
BroType* t = flatten_type((*tl)[0]->Ref());
indices = new TypeList(t);
indices->Append(t->Ref());
}
else
{
BroType* t = merge_types((*tl)[0], (*tl)[1]);
for ( int i = 2; t && i < tl->length(); ++i )
{
BroType* t_new =
merge_types(t, (*tl)[i]);
Unref(t);
t = t_new;
}
if ( ! t )
{
Error("bad set type");
return;
}
indices = new TypeList(t);
indices->Append(t);
}
}
}
}
IMPLEMENT_SERIAL(TableType, SER_TABLE_TYPE);
bool TableType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_TABLE_TYPE, IndexType);
return true;
}
bool TableType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(IndexType);
return true;
}
SetType::~SetType()
{
Unref(elements);
}
IMPLEMENT_SERIAL(SetType, SER_SET_TYPE);
bool SetType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_SET_TYPE, TableType);
SERIALIZE_OPTIONAL(elements);
return true;
}
bool SetType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(TableType);
UNSERIALIZE_OPTIONAL(elements, (ListExpr*) Expr::Unserialize(info, EXPR_LIST));
return true;
}
FuncType::FuncType(RecordType* arg_args, BroType* arg_yield, function_flavor arg_flavor)
: BroType(TYPE_FUNC)
{
args = arg_args;
yield = arg_yield;
flavor = arg_flavor;
arg_types = new TypeList();
bool has_default_arg = false;
for ( int i = 0; i < args->NumFields(); ++i )
{
const TypeDecl* td = args->FieldDecl(i);
if ( td->attrs && td->attrs->FindAttr(ATTR_DEFAULT) )
has_default_arg = true;
else if ( has_default_arg )
{
const char* err_str = fmt("required parameter '%s' must precede "
"default parameters", td->id);
args->Error(err_str);
}
arg_types->Append(args->FieldType(i)->Ref());
}
}
string FuncType::FlavorString() const
{
switch ( flavor ) {
case FUNC_FLAVOR_FUNCTION:
return "function";
case FUNC_FLAVOR_EVENT:
return "event";
case FUNC_FLAVOR_HOOK:
return "hook";
default:
reporter->InternalError("Invalid function flavor");
return "invalid_func_flavor";
}
}
FuncType::~FuncType()
{
Unref(args);
Unref(arg_types);
Unref(yield);
}
BroType* FuncType::YieldType()
{
return yield;
}
const BroType* FuncType::YieldType() const
{
return yield;
}
int FuncType::MatchesIndex(ListExpr*& index) const
{
return check_and_promote_args(index, args) ?
MATCHES_INDEX_SCALAR : DOES_NOT_MATCH_INDEX;
}
int FuncType::CheckArgs(const type_list* args, bool is_init) const
{
const type_list* my_args = arg_types->Types();
if ( my_args->length() != args->length() )
return 0;
for ( int i = 0; i < my_args->length(); ++i )
if ( ! same_type((*args)[i], (*my_args)[i], is_init) )
return 0;
return 1;
}
void FuncType::Describe(ODesc* d) const
{
if ( d->IsReadable() )
{
d->Add(FlavorString());
d->Add("(");
args->DescribeFields(d);
d->Add(")");
if ( yield )
{
d->AddSP(" :");
yield->Describe(d);
}
}
else
{
d->Add(int(Tag()));
d->Add(flavor);
d->Add(yield != 0);
args->DescribeFields(d);
if ( yield )
yield->Describe(d);
}
}
void FuncType::DescribeReST(ODesc* d, bool roles_only) const
{
d->Add(":bro:type:`");
d->Add(FlavorString());
d->Add("`");
d->Add(" (");
args->DescribeFieldsReST(d, true);
d->Add(")");
if ( yield )
{
d->AddSP(" :");
if ( ! yield->GetName().empty() )
{
d->Add(":bro:type:`");
d->Add(yield->GetName());
d->Add("`");
}
else
yield->DescribeReST(d, roles_only);
}
}
IMPLEMENT_SERIAL(FuncType, SER_FUNC_TYPE);
bool FuncType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_FUNC_TYPE, BroType);
assert(args);
assert(arg_types);
SERIALIZE_OPTIONAL(yield);
int ser_flavor = 0;
switch ( flavor ) {
case FUNC_FLAVOR_FUNCTION:
ser_flavor = 0;
break;
case FUNC_FLAVOR_EVENT:
ser_flavor = 1;
break;
case FUNC_FLAVOR_HOOK:
ser_flavor = 2;
break;
default:
reporter->InternalError("Invalid function flavor serialization");
break;
}
return args->Serialize(info) &&
arg_types->Serialize(info) &&
SERIALIZE(ser_flavor);
}
bool FuncType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
UNSERIALIZE_OPTIONAL(yield, BroType::Unserialize(info));
args = (RecordType*) BroType::Unserialize(info, TYPE_RECORD);
if ( ! args )
return false;
arg_types = (TypeList*) BroType::Unserialize(info, TYPE_LIST);
if ( ! arg_types )
return false;
int ser_flavor = 0;
if ( ! UNSERIALIZE(&ser_flavor) )
return false;
switch ( ser_flavor ) {
case 0:
flavor = FUNC_FLAVOR_FUNCTION;
break;
case 1:
flavor = FUNC_FLAVOR_EVENT;
break;
case 2:
flavor = FUNC_FLAVOR_HOOK;
break;
default:
reporter->InternalError("Invalid function flavor unserialization");
break;
}
return true;
}
TypeDecl::TypeDecl(BroType* t, const char* i, attr_list* arg_attrs, bool in_record)
{
type = t;
attrs = arg_attrs ? new Attributes(arg_attrs, t, in_record) : 0;
id = i;
}
TypeDecl::TypeDecl(const TypeDecl& other)
{
type = other.type->Ref();
attrs = other.attrs;
if ( attrs )
::Ref(attrs);
id = copy_string(other.id);
}
TypeDecl::~TypeDecl()
{
Unref(type);
Unref(attrs);
delete [] id;
}
bool TypeDecl::Serialize(SerialInfo* info) const
{
assert(type);
assert(id);
SERIALIZE_OPTIONAL(attrs);
if ( ! (type->Serialize(info) && SERIALIZE(id)) )
return false;
return true;
}
TypeDecl* TypeDecl::Unserialize(UnserialInfo* info)
{
TypeDecl* t = new TypeDecl(0, 0, 0);
UNSERIALIZE_OPTIONAL_STATIC(t->attrs, Attributes::Unserialize(info), t);
t->type = BroType::Unserialize(info);
if ( ! (t->type && UNSERIALIZE_STR(&t->id, 0)) )
{
delete t;
return 0;
}
return t;
}
void TypeDecl::DescribeReST(ODesc* d, bool roles_only) const
{
d->Add(id);
d->Add(": ");
if ( ! type->GetName().empty() )
{
d->Add(":bro:type:`");
d->Add(type->GetName());
d->Add("`");
}
else
type->DescribeReST(d, roles_only);
if ( attrs )
{
d->SP();
attrs->DescribeReST(d);
}
}
RecordType::RecordType(type_decl_list* arg_types) : BroType(TYPE_RECORD)
{
types = arg_types;
num_fields = types ? types->length() : 0;
}
RecordType::~RecordType()
{
if ( types )
{
loop_over_list(*types, i)
delete (*types)[i];
delete types;
}
}
int RecordType::HasField(const char* field) const
{
return FieldOffset(field) >= 0;
}
BroType* RecordType::FieldType(const char* field) const
{
int offset = FieldOffset(field);
return offset >= 0 ? FieldType(offset) : 0;
}
BroType* RecordType::FieldType(int field) const
{
return (*types)[field]->type;
}
Val* RecordType::FieldDefault(int field) const
{
const TypeDecl* td = FieldDecl(field);
if ( ! td->attrs )
return 0;
const Attr* def_attr = td->attrs->FindAttr(ATTR_DEFAULT);
return def_attr ? def_attr->AttrExpr()->Eval(0) : 0;
}
int RecordType::FieldOffset(const char* field) const
{
loop_over_list(*types, i)
{
TypeDecl* td = (*types)[i];
if ( streq(td->id, field) )
return i;
}
return -1;
}
const char* RecordType::FieldName(int field) const
{
return FieldDecl(field)->id;
}
const TypeDecl* RecordType::FieldDecl(int field) const
{
return (*types)[field];
}
TypeDecl* RecordType::FieldDecl(int field)
{
return (*types)[field];
}
void RecordType::Describe(ODesc* d) const
{
d->PushType(this);
if ( d->IsReadable() )
{
if ( d->IsShort() && GetName().size() )
d->Add(GetName());
else
{
d->AddSP("record {");
DescribeFields(d);
d->SP();
d->Add("}");
}
}
else
{
d->Add(int(Tag()));
DescribeFields(d);
}
d->PopType(this);
}
void RecordType::DescribeReST(ODesc* d, bool roles_only) const
{
d->PushType(this);
d->Add(":bro:type:`record`");
if ( num_fields == 0 )
return;
d->NL();
DescribeFieldsReST(d, false);
d->PopType(this);
}
const char* RecordType::AddFields(type_decl_list* others, attr_list* attr)
{
assert(types);
bool log = false;
if ( attr )
{
loop_over_list(*attr, j)
{
if ( (*attr)[j]->Tag() == ATTR_LOG )
log = true;
}
}
loop_over_list(*others, i)
{
TypeDecl* td = (*others)[i];
if ( ! td->FindAttr(ATTR_DEFAULT) &&
! td->FindAttr(ATTR_OPTIONAL) )
return "extension field must be &optional or have &default";
if ( log )
{
if ( ! td->attrs )
td->attrs = new Attributes(new attr_list, td->type, true);
td->attrs->AddAttr(new Attr(ATTR_LOG));
}
types->append(td);
}
delete others;
num_fields = types->length();
return 0;
}
void RecordType::DescribeFields(ODesc* d) const
{
if ( d->IsReadable() )
{
for ( int i = 0; i < num_fields; ++i )
{
if ( i > 0 )
d->SP();
const TypeDecl* td = FieldDecl(i);
d->Add(td->id);
d->Add(":");
if ( d->FindType(td->type) )
d->Add("<recursion>");
else
td->type->Describe(d);
d->Add(";");
}
}
else
{
if ( types )
{
d->AddCount(0);
d->AddCount(types->length());
loop_over_list(*types, i)
{
(*types)[i]->type->Describe(d);
d->SP();
d->Add((*types)[i]->id);
d->SP();
}
}
}
}
void RecordType::DescribeFieldsReST(ODesc* d, bool func_args) const
{
if ( ! func_args )
d->PushIndent();
for ( int i = 0; i < num_fields; ++i )
{
if ( i > 0 )
{
if ( func_args )
d->Add(", ");
else
{
d->NL();
d->NL();
}
}
const TypeDecl* td = FieldDecl(i);
if ( d->FindType(td->type) )
d->Add("<recursion>");
else
td->DescribeReST(d);
if ( func_args )
continue;
using broxygen::IdentifierInfo;
IdentifierInfo* doc = broxygen_mgr->GetIdentifierInfo(GetName());
if ( ! doc )
{
reporter->InternalWarning("Failed to lookup record doc: %s",
GetName().c_str());
continue;
}
string field_from_script = doc->GetDeclaringScriptForField(td->id);
string type_from_script;
if ( doc->GetDeclaringScript() )
type_from_script = doc->GetDeclaringScript()->Name();
if ( ! field_from_script.empty() &&
field_from_script != type_from_script )
{
d->PushIndent();
d->Add(broxygen::redef_indication(field_from_script).c_str());
d->PopIndent();
}
vector<string> cmnts = doc->GetFieldComments(td->id);
if ( cmnts.empty() )
continue;
d->PushIndent();
for ( size_t i = 0; i < cmnts.size(); ++i )
{
if ( i > 0 )
d->NL();
if ( IsFunc(td->type->Tag()) )
{
string s = cmnts[i];
if ( broxygen::prettify_params(s) )
d->NL();
d->Add(s.c_str());
}
else
d->Add(cmnts[i].c_str());
}
d->PopIndentNoNL();
}
if ( ! func_args )
d->PopIndentNoNL();
}
IMPLEMENT_SERIAL(RecordType, SER_RECORD_TYPE)
bool RecordType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_RECORD_TYPE, BroType);
if ( ! SERIALIZE(num_fields) )
return false;
if ( types )
{
if ( ! (SERIALIZE(true) && SERIALIZE(types->length())) )
return false;
loop_over_list(*types, i)
{
if ( ! (*types)[i]->Serialize(info) )
return false;
}
}
else if ( ! SERIALIZE(false) )
return false;
return true;
}
bool RecordType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
if ( ! UNSERIALIZE(&num_fields) )
return false;
bool has_it;
if ( ! UNSERIALIZE(&has_it) )
return false;
if ( has_it )
{
int len;
if ( ! UNSERIALIZE(&len) )
return false;
types = new type_decl_list(len);
while ( len-- )
{
TypeDecl* t = TypeDecl::Unserialize(info);
if ( ! t )
return false;
types->append(t);
}
}
else
types = 0;
return true;
}
SubNetType::SubNetType() : BroType(TYPE_SUBNET)
{
}
void SubNetType::Describe(ODesc* d) const
{
if ( d->IsReadable() )
d->Add("subnet");
else
d->Add(int(Tag()));
}
IMPLEMENT_SERIAL(SubNetType, SER_SUBNET_TYPE);
bool SubNetType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_SUBNET_TYPE, BroType);
return true;
}
bool SubNetType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
return true;
}
FileType::FileType(BroType* yield_type)
: BroType(TYPE_FILE)
{
yield = yield_type;
}
FileType::~FileType()
{
Unref(yield);
}
BroType* FileType::YieldType()
{
return yield;
}
void FileType::Describe(ODesc* d) const
{
if ( d->IsReadable() )
{
d->AddSP("file of");
yield->Describe(d);
}
else
{
d->Add(int(Tag()));
yield->Describe(d);
}
}
IMPLEMENT_SERIAL(FileType, SER_FILE_TYPE);
bool FileType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_FILE_TYPE, BroType);
assert(yield);
return yield->Serialize(info);
}
bool FileType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
yield = BroType::Unserialize(info);
return yield != 0;
}
OpaqueType::OpaqueType(const string& arg_name) : BroType(TYPE_OPAQUE)
{
name = arg_name;
}
void OpaqueType::Describe(ODesc* d) const
{
if ( d->IsReadable() )
d->AddSP("opaque of");
else
d->Add(int(Tag()));
d->Add(name.c_str());
}
void OpaqueType::DescribeReST(ODesc* d, bool roles_only) const
{
d->Add(fmt(":bro:type:`%s` of %s", type_name(Tag()), name.c_str()));
}
IMPLEMENT_SERIAL(OpaqueType, SER_OPAQUE_TYPE);
bool OpaqueType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_OPAQUE_TYPE, BroType);
return SERIALIZE_STR(name.c_str(), name.size());
}
bool OpaqueType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
const char* n;
if ( ! UNSERIALIZE_STR(&n, 0) )
return false;
name = n;
delete [] n;
return true;
}
EnumType::EnumType(const string& name)
: BroType(TYPE_ENUM)
{
counter = 0;
SetName(name);
}
EnumType::EnumType(EnumType* e)
: BroType(TYPE_ENUM)
{
counter = e->counter;
SetName(e->GetName());
for ( NameMap::iterator it = e->names.begin(); it != e->names.end(); ++it )
names[copy_string(it->first)] = it->second;
}
EnumType::~EnumType()
{
for ( NameMap::iterator iter = names.begin(); iter != names.end(); ++iter )
delete [] iter->first;
}
// Note, we use reporter->Error() here (not Error()) to include the current script
// location in the error message, rather than the one where the type was
// originally defined.
void EnumType::AddName(const string& module_name, const char* name, bool is_export, bool deprecated)
{
/* implicit, auto-increment */
if ( counter < 0)
{
reporter->Error("cannot mix explicit enumerator assignment and implicit auto-increment");
SetError();
return;
}
CheckAndAddName(module_name, name, counter, is_export, deprecated);
counter++;
}
void EnumType::AddName(const string& module_name, const char* name, bro_int_t val, bool is_export, bool deprecated)
{
/* explicit value specified */
if ( counter > 0 )
{
reporter->Error("cannot mix explicit enumerator assignment and implicit auto-increment");
SetError();
return;
}
counter = -1;
CheckAndAddName(module_name, name, val, is_export, deprecated);
}
void EnumType::CheckAndAddName(const string& module_name, const char* name,
bro_int_t val, bool is_export, bool deprecated)
{
if ( Lookup(val) )
{
reporter->Error("enumerator value in enumerated type definition already exists");
SetError();
return;
}
ID* id = lookup_ID(name, module_name.c_str());
if ( ! id )
{
id = install_ID(name, module_name.c_str(), true, is_export);
id->SetType(this->Ref());
id->SetEnumConst();
if ( deprecated )
id->MakeDeprecated();
broxygen_mgr->Identifier(id);
}
else
{
// We allow double-definitions if matching exactly. This is so that
// we can define an enum both in a *.bif and *.bro for avoiding
// cyclic dependencies.
if ( id->Name() != make_full_var_name(module_name.c_str(), name)
|| (id->HasVal() && val != id->ID_Val()->AsEnum()) )
{
Unref(id);
reporter->Error("identifier or enumerator value in enumerated type definition already exists");
SetError();
return;
}
Unref(id);
}
AddNameInternal(module_name, name, val, is_export);
set<BroType*> types = BroType::GetAliases(GetName());
set<BroType*>::const_iterator it;
for ( it = types.begin(); it != types.end(); ++it )
if ( *it != this )
(*it)->AsEnumType()->AddNameInternal(module_name, name, val,
is_export);
}
void EnumType::AddNameInternal(const string& module_name, const char* name,
bro_int_t val, bool is_export)
{
string fullname = make_full_var_name(module_name.c_str(), name);
names[copy_string(fullname.c_str())] = val;
}
bro_int_t EnumType::Lookup(const string& module_name, const char* name) const
{
NameMap::const_iterator pos =
names.find(make_full_var_name(module_name.c_str(), name).c_str());
if ( pos == names.end() )
return -1;
else
return pos->second;
}
const char* EnumType::Lookup(bro_int_t value) const
{
for ( NameMap::const_iterator iter = names.begin();
iter != names.end(); ++iter )
if ( iter->second == value )
return iter->first;
return 0;
}
EnumType::enum_name_list EnumType::Names() const
{
enum_name_list n;
for ( NameMap::const_iterator iter = names.begin();
iter != names.end(); ++iter )
n.push_back(std::make_pair(iter->first, iter->second));
return n;
}
void EnumType::DescribeReST(ODesc* d, bool roles_only) const
{
d->Add(":bro:type:`enum`");
// Create temporary, reverse name map so that enums can be documented
// in ascending order of their actual integral value instead of by name.
typedef map< bro_int_t, const char* > RevNameMap;
RevNameMap rev;
for ( NameMap::const_iterator it = names.begin(); it != names.end(); ++it )
rev[it->second] = it->first;
for ( RevNameMap::const_iterator it = rev.begin(); it != rev.end(); ++it )
{
d->NL();
d->PushIndent();
if ( roles_only )
d->Add(fmt(":bro:enum:`%s`", it->second));
else
d->Add(fmt(".. bro:enum:: %s %s", it->second, GetName().c_str()));
using broxygen::IdentifierInfo;
IdentifierInfo* doc = broxygen_mgr->GetIdentifierInfo(it->second);
if ( ! doc )
{
reporter->InternalWarning("Enum %s documentation lookup failure",
it->second);
continue;
}
string enum_from_script;
string type_from_script;
if ( doc->GetDeclaringScript() )
enum_from_script = doc->GetDeclaringScript()->Name();
IdentifierInfo* type_doc = broxygen_mgr->GetIdentifierInfo(GetName());
if ( type_doc && type_doc->GetDeclaringScript() )
type_from_script = type_doc->GetDeclaringScript()->Name();
if ( ! enum_from_script.empty() &&
enum_from_script != type_from_script )
{
d->NL();
d->PushIndent();
d->Add(broxygen::redef_indication(enum_from_script).c_str());
d->PopIndent();
}
vector<string> cmnts = doc->GetComments();
if ( cmnts.empty() )
{
d->PopIndentNoNL();
continue;
}
d->NL();
d->PushIndent();
for ( size_t i = 0; i < cmnts.size(); ++i )
{
if ( i > 0 )
d->NL();
d->Add(cmnts[i].c_str());
}
d->PopIndentNoNL();
d->PopIndentNoNL();
}
}
IMPLEMENT_SERIAL(EnumType, SER_ENUM_TYPE);
bool EnumType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_ENUM_TYPE, BroType);
if ( ! (SERIALIZE(counter) && SERIALIZE((unsigned int) names.size()) &&
// Dummy boolean for backwards compatibility.
SERIALIZE(false)) )
return false;
for ( NameMap::const_iterator iter = names.begin();
iter != names.end(); ++iter )
{
if ( ! SERIALIZE(iter->first) || ! SERIALIZE(iter->second) )
return false;
}
return true;
}
bool EnumType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
unsigned int len;
bool dummy;
if ( ! UNSERIALIZE(&counter) ||
! UNSERIALIZE(&len) ||
// Dummy boolean for backwards compatibility.
! UNSERIALIZE(&dummy) )
return false;
while ( len-- )
{
const char* name;
bro_int_t val;
if ( ! (UNSERIALIZE_STR(&name, 0) && UNSERIALIZE(&val)) )
return false;
names[name] = val;
}
return true;
}
VectorType::VectorType(BroType* element_type)
: BroType(TYPE_VECTOR), yield_type(element_type)
{
}
VectorType::~VectorType()
{
Unref(yield_type);
}
BroType* VectorType::YieldType()
{
// Work around the fact that we use void internally to mark a vector
// as being unspecified. When looking at its yield type, we need to
// return any as that's what other code historically expects for type
// comparisions.
if ( IsUnspecifiedVector() )
{
BroType* ret = ::base_type(TYPE_ANY);
Unref(ret); // unref, because this won't be held by anyone.
assert(ret);
return ret;
}
return yield_type;
}
const BroType* VectorType::YieldType() const
{
// Work around the fact that we use void internally to mark a vector
// as being unspecified. When looking at its yield type, we need to
// return any as that's what other code historically expects for type
// comparisions.
if ( IsUnspecifiedVector() )
{
BroType* ret = ::base_type(TYPE_ANY);
Unref(ret); // unref, because this won't be held by anyone.
assert(ret);
return ret;
}
return yield_type;
}
int VectorType::MatchesIndex(ListExpr*& index) const
{
expr_list& el = index->Exprs();
if ( el.length() != 1 )
return DOES_NOT_MATCH_INDEX;
if ( el[0]->Type()->Tag() == TYPE_VECTOR )
return (IsIntegral(el[0]->Type()->YieldType()->Tag()) ||
IsBool(el[0]->Type()->YieldType()->Tag())) ?
MATCHES_INDEX_VECTOR : DOES_NOT_MATCH_INDEX;
else
return (IsIntegral(el[0]->Type()->Tag()) ||
IsBool(el[0]->Type()->Tag())) ?
MATCHES_INDEX_SCALAR : DOES_NOT_MATCH_INDEX;
}
bool VectorType::IsUnspecifiedVector() const
{
return yield_type->Tag() == TYPE_VOID;
}
IMPLEMENT_SERIAL(VectorType, SER_VECTOR_TYPE);
bool VectorType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_VECTOR_TYPE, BroType);
return yield_type->Serialize(info);
}
bool VectorType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
yield_type = BroType::Unserialize(info);
return yield_type != 0;
}
void VectorType::Describe(ODesc* d) const
{
if ( d->IsReadable() )
d->AddSP("vector of");
else
d->Add(int(Tag()));
yield_type->Describe(d);
}
void VectorType::DescribeReST(ODesc* d, bool roles_only) const
{
d->Add(fmt(":bro:type:`%s` of ", type_name(Tag())));
if ( yield_type->GetName().empty() )
yield_type->DescribeReST(d, roles_only);
else
d->Add(fmt(":bro:type:`%s`", yield_type->GetName().c_str()));
}
BroType* base_type_no_ref(TypeTag tag)
{
static BroType* base_types[NUM_TYPES];
// We could check here that "tag" actually corresponds to a BRO
// basic type.
int t = int(tag);
if ( ! base_types[t] )
{
base_types[t] = new BroType(tag, true);
// Give the base types a pseudo-location for easier identification.
Location l(type_name(tag), 0, 0, 0, 0);
base_types[t]->SetLocationInfo(&l);
}
return base_types[t];
}
// Returns true if t1 is initialization-compatible with t2 (i.e., if an
// initializer with type t1 can be used to initialize a value with type t2),
// false otherwise. Assumes that t1's tag is different from t2's. Note
// that the test is in only one direction - we don't check whether t2 is
// initialization-compatible with t1.
static int is_init_compat(const BroType* t1, const BroType* t2)
{
if ( t1->Tag() == TYPE_LIST )
{
if ( t2->Tag() == TYPE_RECORD )
return 1;
else
return t1->AsTypeList()->AllMatch(t2, 1);
}
if ( t1->IsSet() )
return same_type(t1->AsSetType()->Indices(), t2, 1);
return 0;
}
int same_type(const BroType* t1, const BroType* t2, int is_init, bool match_record_field_names)
{
if ( t1 == t2 ||
t1->Tag() == TYPE_ANY ||
t2->Tag() == TYPE_ANY )
return 1;
t1 = flatten_type(t1);
t2 = flatten_type(t2);
if ( t1 == t2 )
return 1;
if ( t1->Tag() != t2->Tag() )
{
if ( is_init )
return is_init_compat(t1, t2) || is_init_compat(t2, t1);
return 0;
}
switch ( t1->Tag() ) {
case TYPE_VOID:
case TYPE_BOOL:
case TYPE_INT:
case TYPE_COUNT:
case TYPE_COUNTER:
case TYPE_DOUBLE:
case TYPE_TIME:
case TYPE_INTERVAL:
case TYPE_STRING:
case TYPE_PATTERN:
case TYPE_TIMER:
case TYPE_PORT:
case TYPE_ADDR:
case TYPE_SUBNET:
case TYPE_ANY:
case TYPE_ERROR:
return 1;
case TYPE_ENUM:
// We should probably check to see whether all of the
// enumerations are present and in the same location.
// FIXME: Yes, but perhaps we should better return
// true per default?
return 1;
case TYPE_TABLE:
{
const IndexType* it1 = (const IndexType*) t1;
const IndexType* it2 = (const IndexType*) t2;
TypeList* tl1 = it1->Indices();
TypeList* tl2 = it2->Indices();
if ( tl1 || tl2 )
{
if ( ! tl1 || ! tl2 || ! same_type(tl1, tl2, is_init, match_record_field_names) )
return 0;
}
const BroType* y1 = t1->YieldType();
const BroType* y2 = t2->YieldType();
if ( y1 || y2 )
{
if ( ! y1 || ! y2 || ! same_type(y1, y2, is_init, match_record_field_names) )
return 0;
}
return 1;
}
case TYPE_FUNC:
{
const FuncType* ft1 = (const FuncType*) t1;
const FuncType* ft2 = (const FuncType*) t2;
if ( ft1->Flavor() != ft2->Flavor() )
return 0;
if ( t1->YieldType() || t2->YieldType() )
{
if ( ! t1->YieldType() || ! t2->YieldType() ||
! same_type(t1->YieldType(), t2->YieldType(), is_init, match_record_field_names) )
return 0;
}
return ft1->CheckArgs(ft2->ArgTypes()->Types(), is_init);
}
case TYPE_RECORD:
{
const RecordType* rt1 = (const RecordType*) t1;
const RecordType* rt2 = (const RecordType*) t2;
if ( rt1->NumFields() != rt2->NumFields() )
return 0;
for ( int i = 0; i < rt1->NumFields(); ++i )
{
const TypeDecl* td1 = rt1->FieldDecl(i);
const TypeDecl* td2 = rt2->FieldDecl(i);
if ( (match_record_field_names && ! streq(td1->id, td2->id)) ||
! same_type(td1->type, td2->type, is_init, match_record_field_names) )
return 0;
}
return 1;
}
case TYPE_LIST:
{
const type_list* tl1 = t1->AsTypeList()->Types();
const type_list* tl2 = t2->AsTypeList()->Types();
if ( tl1->length() != tl2->length() )
return 0;
loop_over_list(*tl1, i)
if ( ! same_type((*tl1)[i], (*tl2)[i], is_init, match_record_field_names) )
return 0;
return 1;
}
case TYPE_VECTOR:
case TYPE_FILE:
return same_type(t1->YieldType(), t2->YieldType(), is_init, match_record_field_names);
case TYPE_OPAQUE:
{
const OpaqueType* ot1 = (const OpaqueType*) t1;
const OpaqueType* ot2 = (const OpaqueType*) t2;
return ot1->Name() == ot2->Name() ? 1 : 0;
}
case TYPE_TYPE:
return same_type(t1, t2, is_init, match_record_field_names);
case TYPE_UNION:
reporter->Error("union type in same_type()");
}
return 0;
}
int same_attrs(const Attributes* a1, const Attributes* a2)
{
if ( ! a1 )
return (a2 == 0);
if ( ! a2 )
return (a1 == 0);
return (*a1 == *a2);
}
int record_promotion_compatible(const RecordType* super_rec,
const RecordType* sub_rec)
{
for ( int i = 0; i < sub_rec->NumFields(); ++i )
{
int o = super_rec->FieldOffset(sub_rec->FieldName(i));
if ( o < 0 )
// Orphaned field.
continue;
BroType* sub_field_type = sub_rec->FieldType(i);
BroType* super_field_type = super_rec->FieldType(o);
if ( same_type(sub_field_type, super_field_type) )
continue;
if ( sub_field_type->Tag() != TYPE_RECORD )
return 0;
if ( super_field_type->Tag() != TYPE_RECORD )
return 0;
if ( ! record_promotion_compatible(super_field_type->AsRecordType(),
sub_field_type->AsRecordType()) )
return 0;
}
return 1;
}
const BroType* flatten_type(const BroType* t)
{
if ( t->Tag() != TYPE_LIST )
return t;
const TypeList* tl = t->AsTypeList();
if ( tl->IsPure() )
return tl->PureType();
const type_list* types = tl->Types();
if ( types->length() == 0 )
reporter->InternalError("empty type list in flatten_type");
const BroType* ft = (*types)[0];
if ( types->length() == 1 || tl->AllMatch(ft, 0) )
return ft;
return t;
}
BroType* flatten_type(BroType* t)
{
return (BroType*) flatten_type((const BroType*) t);
}
int is_assignable(BroType* t)
{
switch ( t->Tag() ) {
case TYPE_BOOL:
case TYPE_INT:
case TYPE_COUNT:
case TYPE_COUNTER:
case TYPE_DOUBLE:
case TYPE_TIME:
case TYPE_INTERVAL:
case TYPE_STRING:
case TYPE_PATTERN:
case TYPE_ENUM:
case TYPE_TIMER:
case TYPE_PORT:
case TYPE_ADDR:
case TYPE_SUBNET:
case TYPE_RECORD:
case TYPE_FUNC:
case TYPE_ANY:
case TYPE_ERROR:
case TYPE_LIST:
return 1;
case TYPE_VECTOR:
case TYPE_FILE:
case TYPE_OPAQUE:
case TYPE_TABLE:
case TYPE_TYPE:
return 1;
case TYPE_VOID:
return 0;
case TYPE_UNION:
reporter->Error("union type in is_assignable()");
}
return 0;
}
TypeTag max_type(TypeTag t1, TypeTag t2)
{
if ( t1 == TYPE_INTERVAL || t1 == TYPE_TIME )
t1 = TYPE_DOUBLE;
if ( t2 == TYPE_INTERVAL || t2 == TYPE_TIME )
t2 = TYPE_DOUBLE;
if ( BothArithmetic(t1, t2) )
{
#define CHECK_TYPE(t) \
if ( t1 == t || t2 == t ) \
return t;
CHECK_TYPE(TYPE_DOUBLE);
CHECK_TYPE(TYPE_INT);
CHECK_TYPE(TYPE_COUNT);
// Note - mixing two TYPE_COUNTER's still promotes to
// a TYPE_COUNT.
return TYPE_COUNT;
}
else
{
reporter->InternalError("non-arithmetic tags in max_type()");
return TYPE_ERROR;
}
}
BroType* merge_types(const BroType* t1, const BroType* t2)
{
t1 = flatten_type(t1);
t2 = flatten_type(t2);
TypeTag tg1 = t1->Tag();
TypeTag tg2 = t2->Tag();
if ( BothArithmetic(tg1, tg2) )
return base_type(max_type(tg1, tg2));
if ( tg1 != tg2 )
{
t1->Error("incompatible types", t2);
return 0;
}
switch ( tg1 ) {
case TYPE_TIME:
case TYPE_INTERVAL:
case TYPE_STRING:
case TYPE_PATTERN:
case TYPE_TIMER:
case TYPE_PORT:
case TYPE_ADDR:
case TYPE_SUBNET:
case TYPE_BOOL:
case TYPE_ANY:
case TYPE_ERROR:
return base_type(tg1);
case TYPE_TABLE:
{
const IndexType* it1 = (const IndexType*) t1;
const IndexType* it2 = (const IndexType*) t2;
const type_list* tl1 = it1->IndexTypes();
const type_list* tl2 = it2->IndexTypes();
TypeList* tl3 = 0;
if ( tl1 || tl2 )
{
if ( ! tl1 || ! tl2 || tl1->length() != tl2->length() )
{
t1->Error("incompatible types", t2);
return 0;
}
tl3 = new TypeList();
loop_over_list(*tl1, i)
{
BroType* tl3_i = merge_types((*tl1)[i], (*tl2)[i]);
if ( ! tl3_i )
{
Unref(tl3);
return 0;
}
tl3->Append(tl3_i);
}
}
const BroType* y1 = t1->YieldType();
const BroType* y2 = t2->YieldType();
BroType* y3 = 0;
if ( y1 || y2 )
{
if ( ! y1 || ! y2 )
{
t1->Error("incompatible types", t2);
Unref(tl3);
return 0;
}
y3 = merge_types(y1, y2);
if ( ! y3 )
{
Unref(tl3);
return 0;
}
}
if ( t1->IsSet() )
return new SetType(tl3, 0);
else
return new TableType(tl3, y3);
}
case TYPE_FUNC:
{
if ( ! same_type(t1, t2) )
{
t1->Error("incompatible types", t2);
return 0;
}
const FuncType* ft1 = (const FuncType*) t1;
const FuncType* ft2 = (const FuncType*) t1;
BroType* args = merge_types(ft1->Args(), ft2->Args());
BroType* yield = t1->YieldType() ?
merge_types(t1->YieldType(), t2->YieldType()) : 0;
return new FuncType(args->AsRecordType(), yield, ft1->Flavor());
}
case TYPE_RECORD:
{
const RecordType* rt1 = (const RecordType*) t1;
const RecordType* rt2 = (const RecordType*) t2;
if ( rt1->NumFields() != rt2->NumFields() )
return 0;
type_decl_list* tdl3 = new type_decl_list;
for ( int i = 0; i < rt1->NumFields(); ++i )
{
const TypeDecl* td1 = rt1->FieldDecl(i);
const TypeDecl* td2 = rt2->FieldDecl(i);
BroType* tdl3_i = merge_types(td1->type, td2->type);
if ( ! streq(td1->id, td2->id) || ! tdl3_i )
{
t1->Error("incompatible record fields", t2);
delete tdl3;
Unref(tdl3_i);
return 0;
}
tdl3->append(new TypeDecl(tdl3_i, copy_string(td1->id)));
}
return new RecordType(tdl3);
}
case TYPE_LIST:
{
const TypeList* tl1 = t1->AsTypeList();
const TypeList* tl2 = t2->AsTypeList();
if ( tl1->IsPure() != tl2->IsPure() )
{
tl1->Error("incompatible lists", tl2);
return 0;
}
const type_list* l1 = tl1->Types();
const type_list* l2 = tl2->Types();
if ( l1->length() == 0 || l2->length() == 0 )
{
if ( l1->length() == 0 )
tl1->Error("empty list");
else
tl2->Error("empty list");
return 0;
}
if ( tl1->IsPure() )
{
// We will be expanding the pure list when converting
// the initialization expression into a set of values.
// So the merge type of the list is the type of one
// of the elements, providing they're consistent.
return merge_types((*l1)[0], (*l2)[0]);
}
// Impure lists - must have the same size and match element
// by element.
if ( l1->length() != l2->length() )
{
tl1->Error("different number of indices", tl2);
return 0;
}
TypeList* tl3 = new TypeList();
loop_over_list(*l1, i)
tl3->Append(merge_types((*l1)[i], (*l2)[i]));
return tl3;
}
case TYPE_VECTOR:
if ( ! same_type(t1->YieldType(), t2->YieldType()) )
{
t1->Error("incompatible types", t2);
return 0;
}
return new VectorType(merge_types(t1->YieldType(), t2->YieldType()));
case TYPE_FILE:
if ( ! same_type(t1->YieldType(), t2->YieldType()) )
{
t1->Error("incompatible types", t2);
return 0;
}
return new FileType(merge_types(t1->YieldType(), t2->YieldType()));
case TYPE_UNION:
reporter->InternalError("union type in merge_types()");
return 0;
default:
reporter->InternalError("bad type in merge_types()");
return 0;
}
}
BroType* merge_type_list(ListExpr* elements)
{
TypeList* tl_type = elements->Type()->AsTypeList();
type_list* tl = tl_type->Types();
if ( tl->length() < 1 )
{
reporter->Error("no type can be inferred for empty list");
return 0;
}
BroType* t = (*tl)[0]->Ref();
if ( tl->length() == 1 )
return t;
for ( int i = 1; t && i < tl->length(); ++i )
{
BroType* t_new = merge_types(t, (*tl)[i]);
Unref(t);
t = t_new;
}
if ( ! t )
reporter->Error("inconsistent types in list");
return t;
}
// Reduces an aggregate type.
static BroType* reduce_type(BroType* t)
{
if ( t->Tag() == TYPE_LIST )
return flatten_type(t);
else if ( t->IsSet() )
{
TypeList* tl = t->AsTableType()->Indices();
if ( tl->Types()->length() == 1 )
return (*tl->Types())[0];
else
return tl;
}
else
return t;
}
BroType* init_type(Expr* init)
{
if ( init->Tag() != EXPR_LIST )
{
BroType* t = init->InitType();
if ( ! t )
return 0;
if ( t->Tag() == TYPE_LIST &&
t->AsTypeList()->Types()->length() != 1 )
{
init->Error("list used in scalar initialization");
Unref(t);
return 0;
}
return t;
}
ListExpr* init_list = init->AsListExpr();
const expr_list& el = init_list->Exprs();
if ( el.length() == 0 )
{
init->Error("empty list in untyped initialization");
return 0;
}
// Could be a record, a set, or a list of table elements.
Expr* e0 = el[0];
if ( e0->IsRecordElement(0) )
// ListExpr's know how to build a record from their
// components.
return init_list->InitType();
BroType* t = e0->InitType();
if ( t )
t = reduce_type(t);
if ( ! t )
return 0;
for ( int i = 1; t && i < el.length(); ++i )
{
BroType* el_t = el[i]->InitType();
BroType* ti = el_t ? reduce_type(el_t) : 0;
if ( ! ti )
{
Unref(t);
return 0;
}
if ( same_type(t, ti) )
{
Unref(ti);
continue;
}
BroType* t_merge = merge_types(t, ti);
Unref(t);
Unref(ti);
t = t_merge;
}
if ( ! t )
{
init->Error("type error in initialization");
return 0;
}
if ( t->Tag() == TYPE_TABLE && ! t->AsTableType()->IsSet() )
// A list of table elements.
return t;
// A set. If the index type isn't yet a type list, make
// it one, as that's what's required for creating a set type.
if ( t->Tag() != TYPE_LIST )
{
TypeList* tl = new TypeList(t);
tl->Append(t);
t = tl;
}
return new SetType(t->AsTypeList(), 0);
}
bool is_atomic_type(const BroType* t)
{
switch ( t->InternalType() ) {
case TYPE_INTERNAL_INT:
case TYPE_INTERNAL_UNSIGNED:
case TYPE_INTERNAL_DOUBLE:
case TYPE_INTERNAL_STRING:
case TYPE_INTERNAL_ADDR:
case TYPE_INTERNAL_SUBNET:
return true;
default:
return false;
}
}