// See the file "COPYING" in the main distribution directory for copyright. #include "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 #include #include // Note: This function must be thread-safe. const char* type_name(TypeTag t) { static const char* type_names[int(NUM_TYPES)] = { "void", "bool", "int", "count", "counter", "double", "time", "interval", "string", "pattern", "enum", "timer", "port", "addr", "subnet", "any", "table", "union", "record", "types", "func", "file", "opaque", "vector", "type", "error", }; 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 = 0; uint32 len = form->EndWrite(&data); form->StartRead(data, len); UnserialInfo uinfo(&ss); uinfo.cache = false; BroType* rval = this->Unserialize(&uinfo, false); assert(rval != this); delete [] 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; } 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; } 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 { if ( d->IsReadable() ) { d->AddSP("record {"); DescribeFields(d); d->SP(); d->Add("}"); } else { d->Add(int(Tag())); DescribeFields(d); } } void RecordType::DescribeReST(ODesc* d, bool roles_only) const { d->Add(":bro:type:`record`"); if ( num_fields == 0 ) return; d->NL(); DescribeFieldsReST(d, false); } 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(":"); 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); td->DescribeReST(d); if ( func_args ) continue; using broxygen::IdentifierDocument; IdentifierDocument* doc = broxygen_mgr->GetIdentifierDoc(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(fmt("(from ``redef`` in :doc:`/scripts/%s`)", field_from_script.c_str())); d->PopIndent(); } vector 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(); 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()); } 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() { 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) { /* 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); counter++; } void EnumType::AddName(const string& module_name, const char* name, bro_int_t val, bool is_export) { /* 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); } void EnumType::CheckAndAddName(const string& module_name, const char* name, bro_int_t val, bool is_export) { 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(); broxygen_mgr->Identifier(id); } else { reporter->Error("identifier or enumerator value in enumerated type definition already exists"); SetError(); return; } AddNameInternal(module_name, name, val, is_export); set types = type_aliases[GetName()]; set::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) { NameMap::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) { for ( NameMap::iterator iter = names.begin(); iter != names.end(); ++iter ) if ( iter->second == value ) return iter->first; return 0; } 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::IdentifierDocument; IdentifierDocument* doc = broxygen_mgr->GetIdentifierDoc(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(); IdentifierDocument* type_doc = broxygen_mgr->GetIdentifierDoc(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(fmt("(from ``redef`` in :doc:`/scripts/%s`)", enum_from_script.c_str())); d->PopIndent(); } vector 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); } 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_ANY; } 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); } BroType* base_type(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]->Ref(); } // 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) { 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) ) return 0; } const BroType* y1 = t1->YieldType(); const BroType* y2 = t2->YieldType(); if ( y1 || y2 ) { if ( ! y1 || ! y2 || ! same_type(y1, y2, is_init) ) 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) ) 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 ( ! streq(td1->id, td2->id) || ! same_type(td1->type, td2->type, is_init) ) 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) ) return 0; return 1; } case TYPE_VECTOR: case TYPE_FILE: return same_type(t1->YieldType(), t2->YieldType(), is_init); 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); 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 */) { #if 0 int n = sub_rec->NumFields(); for ( int i = 0; i < n; ++i ) { if ( ! super_rec->HasField(sub_rec->FieldName(i)) ) return 0; } #endif 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; } }