mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00

Which was used by the new ShallowClone method and causes multiple enum declarations/definitions to now crash. Such code is not typically seen in scripts, but being able to define an enum in both .bif and .bro files has historically been supported.
2106 lines
40 KiB
C++
2106 lines
40 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "zeek-config.h"
|
|
|
|
#include "Type.h"
|
|
#include "Attr.h"
|
|
#include "Expr.h"
|
|
#include "Scope.h"
|
|
#include "Reporter.h"
|
|
#include "zeekygen/Manager.h"
|
|
#include "zeekygen/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::ShallowClone()
|
|
{
|
|
switch ( 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:
|
|
return new BroType(tag, base_type);
|
|
|
|
default:
|
|
reporter->InternalError("cloning illegal base BroType");
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
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(":zeek:type:`%s`", type_name(Tag())));
|
|
}
|
|
|
|
void BroType::SetError()
|
|
{
|
|
tag = TYPE_ERROR;
|
|
}
|
|
|
|
unsigned int BroType::MemoryAllocation() const
|
|
{
|
|
return padded_sizeof(*this);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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(":zeek: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(":zeek: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(":zeek: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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
TableType* TableType::ShallowClone()
|
|
{
|
|
if ( indices )
|
|
indices->Ref();
|
|
if ( yield_type )
|
|
yield_type->Ref();
|
|
|
|
return new TableType(indices, yield_type);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SetType* SetType::ShallowClone()
|
|
{
|
|
// constructor only consumes indices when elements
|
|
// is set
|
|
if ( elements && indices )
|
|
{
|
|
elements->Ref();
|
|
indices->Ref();
|
|
}
|
|
|
|
return new SetType(indices, elements);
|
|
}
|
|
|
|
SetType::~SetType()
|
|
{
|
|
Unref(elements);
|
|
}
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
FuncType* FuncType::ShallowClone()
|
|
{
|
|
auto f = new FuncType();
|
|
f->args = args->Ref()->AsRecordType();
|
|
f->arg_types = arg_types->Ref()->AsTypeList();
|
|
f->yield = yield->Ref();
|
|
f->flavor = flavor;
|
|
return f;
|
|
}
|
|
|
|
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(":zeek:type:`");
|
|
d->Add(FlavorString());
|
|
d->Add("`");
|
|
d->Add(" (");
|
|
args->DescribeFieldsReST(d, true);
|
|
d->Add(")");
|
|
|
|
if ( yield )
|
|
{
|
|
d->AddSP(" :");
|
|
|
|
if ( ! yield->GetName().empty() )
|
|
{
|
|
d->Add(":zeek:type:`");
|
|
d->Add(yield->GetName());
|
|
d->Add("`");
|
|
}
|
|
else
|
|
yield->DescribeReST(d, roles_only);
|
|
}
|
|
}
|
|
|
|
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, false) : 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;
|
|
}
|
|
|
|
void TypeDecl::DescribeReST(ODesc* d, bool roles_only) const
|
|
{
|
|
d->Add(id);
|
|
d->Add(": ");
|
|
|
|
if ( ! type->GetName().empty() )
|
|
{
|
|
d->Add(":zeek: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;
|
|
}
|
|
|
|
// in this case the clone is actually not so shallow, since
|
|
// it gets modified by everyone.
|
|
RecordType* RecordType::ShallowClone()
|
|
{
|
|
auto pass = new type_decl_list();
|
|
loop_over_list(*types, i)
|
|
pass->append(new TypeDecl(*(*types)[i]));
|
|
return new RecordType(pass);
|
|
}
|
|
|
|
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(":zeek: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, false);
|
|
|
|
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
|
|
{
|
|
if ( num_fields == 1 && streq(td->id, "va_args") &&
|
|
td->type->Tag() == TYPE_ANY )
|
|
// This was a BIF using variable argument list
|
|
d->Add("...");
|
|
else
|
|
td->DescribeReST(d);
|
|
}
|
|
|
|
if ( func_args )
|
|
continue;
|
|
|
|
using zeekygen::IdentifierInfo;
|
|
IdentifierInfo* doc = zeekygen_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(zeekygen::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 ( zeekygen::prettify_params(s) )
|
|
d->NL();
|
|
|
|
d->Add(s.c_str());
|
|
}
|
|
else
|
|
d->Add(cmnts[i].c_str());
|
|
}
|
|
|
|
d->PopIndentNoNL();
|
|
}
|
|
|
|
if ( ! func_args )
|
|
d->PopIndentNoNL();
|
|
}
|
|
|
|
string RecordType::GetFieldDeprecationWarning(int field, bool has_check) const
|
|
{
|
|
const TypeDecl* decl = FieldDecl(field);
|
|
if ( decl)
|
|
{
|
|
string result;
|
|
if ( const Attr* deprecation = decl->FindAttr(ATTR_DEPRECATED) )
|
|
{
|
|
ConstExpr* expr = static_cast<ConstExpr*>(deprecation->AttrExpr());
|
|
if ( expr )
|
|
{
|
|
StringVal* text = expr->Value()->AsStringVal();
|
|
result = text->CheckString();
|
|
}
|
|
}
|
|
|
|
if ( result.empty() )
|
|
return fmt("deprecated (%s%s$%s)", GetName().c_str(), has_check ? "?" : "",
|
|
FieldName(field));
|
|
else
|
|
return fmt("deprecated (%s%s$%s): %s", GetName().c_str(), has_check ? "?" : "",
|
|
FieldName(field), result.c_str());
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
SubNetType::SubNetType() : BroType(TYPE_SUBNET)
|
|
{
|
|
}
|
|
|
|
void SubNetType::Describe(ODesc* d) const
|
|
{
|
|
if ( d->IsReadable() )
|
|
d->Add("subnet");
|
|
else
|
|
d->Add(int(Tag()));
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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(":zeek:type:`%s` of %s", type_name(Tag()), name.c_str()));
|
|
}
|
|
|
|
EnumType::EnumType(const string& name)
|
|
: BroType(TYPE_ENUM)
|
|
{
|
|
counter = 0;
|
|
SetName(name);
|
|
}
|
|
|
|
EnumType::EnumType(const EnumType* e)
|
|
: BroType(TYPE_ENUM)
|
|
{
|
|
counter = e->counter;
|
|
SetName(e->GetName());
|
|
|
|
for ( auto it = e->names.begin(); it != e->names.end(); ++it )
|
|
names[it->first] = it->second;
|
|
|
|
vals = e->vals;
|
|
|
|
for ( auto& kv : vals )
|
|
::Ref(kv.second);
|
|
}
|
|
|
|
EnumType* EnumType::ShallowClone()
|
|
{
|
|
if ( counter == 0 )
|
|
return new EnumType(GetName());
|
|
|
|
return new EnumType(this);
|
|
}
|
|
|
|
EnumType::~EnumType()
|
|
{
|
|
for ( auto& kv : vals )
|
|
Unref(kv.second);
|
|
}
|
|
|
|
// 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, Expr* deprecation)
|
|
{
|
|
/* 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, deprecation);
|
|
counter++;
|
|
}
|
|
|
|
void EnumType::AddName(const string& module_name, const char* name, bro_int_t val, bool is_export, Expr* deprecation)
|
|
{
|
|
/* 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, deprecation);
|
|
}
|
|
|
|
void EnumType::CheckAndAddName(const string& module_name, const char* name,
|
|
bro_int_t val, bool is_export, Expr* deprecation)
|
|
{
|
|
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 ( deprecation )
|
|
id->MakeDeprecated(deprecation);
|
|
|
|
zeekygen_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 *.zeek for avoiding
|
|
// cyclic dependencies.
|
|
string fullname = make_full_var_name(module_name.c_str(), name);
|
|
if ( id->Name() != fullname
|
|
|| (id->HasVal() && val != id->ID_Val()->AsEnum())
|
|
|| (names.find(fullname) != names.end() && names[fullname] != val) )
|
|
{
|
|
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);
|
|
|
|
if ( vals.find(val) == vals.end() )
|
|
vals[val] = new EnumVal(this, val);
|
|
|
|
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[fullname] = 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.c_str();
|
|
|
|
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;
|
|
}
|
|
|
|
EnumVal* EnumType::GetVal(bro_int_t i)
|
|
{
|
|
auto it = vals.find(i);
|
|
EnumVal* rval;
|
|
|
|
if ( it == vals.end() )
|
|
{
|
|
rval = new EnumVal(this, i);
|
|
vals[i] = rval;
|
|
}
|
|
else
|
|
rval = it->second;
|
|
|
|
::Ref(rval);
|
|
return rval;
|
|
}
|
|
|
|
void EnumType::DescribeReST(ODesc* d, bool roles_only) const
|
|
{
|
|
d->Add(":zeek: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, std::string> 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(":zeek:enum:`%s`", it->second.c_str()));
|
|
else
|
|
d->Add(fmt(".. zeek:enum:: %s %s", it->second.c_str(), GetName().c_str()));
|
|
|
|
using zeekygen::IdentifierInfo;
|
|
IdentifierInfo* doc = zeekygen_mgr->GetIdentifierInfo(it->second);
|
|
|
|
if ( ! doc )
|
|
{
|
|
reporter->InternalWarning("Enum %s documentation lookup failure",
|
|
it->second.c_str());
|
|
continue;
|
|
}
|
|
|
|
string enum_from_script;
|
|
string type_from_script;
|
|
|
|
if ( doc->GetDeclaringScript() )
|
|
enum_from_script = doc->GetDeclaringScript()->Name();
|
|
|
|
IdentifierInfo* type_doc = zeekygen_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(zeekygen::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();
|
|
}
|
|
}
|
|
|
|
VectorType::VectorType(BroType* element_type)
|
|
: BroType(TYPE_VECTOR), yield_type(element_type)
|
|
{
|
|
}
|
|
|
|
VectorType* VectorType::ShallowClone()
|
|
{
|
|
return new VectorType(yield_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 && el.length() != 2)
|
|
return DOES_NOT_MATCH_INDEX;
|
|
|
|
if ( el.length() == 2 )
|
|
return MATCHES_INDEX_VECTOR;
|
|
else 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;
|
|
}
|
|
|
|
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(":zeek:type:`%s` of ", type_name(Tag())));
|
|
|
|
if ( yield_type->GetName().empty() )
|
|
yield_type->DescribeReST(d, roles_only);
|
|
else
|
|
d->Add(fmt(":zeek: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;
|
|
}
|
|
|
|
#define CHECK_TYPE(t) \
|
|
if ( t1 == t || t2 == t ) \
|
|
return t;
|
|
|
|
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) )
|
|
{
|
|
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(rt1->NumFields());
|
|
|
|
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;
|
|
}
|
|
}
|