zeek/src/Type.cc
Jon Siwek db5b73cb8a Fix ref-counting bug in EnumType copy ctor
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.
2019-07-15 12:42:56 -07:00

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;
}
}