diff --git a/NEWS b/NEWS index 46b2831179..349aeb51b9 100644 --- a/NEWS +++ b/NEWS @@ -148,6 +148,9 @@ Deprecated Functionality - Marked the Continuation.h and PacketDumper.h files as deprecated. The code contained within them is unused by Zeek. +- ``Type::GetAliases()`` and ``Type::AddAlias()`` are deprecated, use + ``Type::Aliases()`` and ``Type::RegisterAlias()``. + Zeek 3.2.0 ========== diff --git a/src/Type.cc b/src/Type.cc index 86c8caaf59..eac80264e8 100644 --- a/src/Type.cc +++ b/src/Type.cc @@ -1348,13 +1348,11 @@ void EnumType::CheckAndAddName(const string& module_name, const char* name, if ( vals.find(val) == vals.end() ) vals[val] = make_intrusive(IntrusivePtr{NewRef{}, this}, val); - set types = Type::GetAliases(GetName()); - set::const_iterator it; + const auto& types = Type::Aliases(GetName()); - for ( it = types.begin(); it != types.end(); ++it ) - if ( *it != this ) - (*it)->AsEnumType()->AddNameInternal(module_name, name, val, - is_export); + for ( const auto& t : types ) + if ( t.get() != this ) + t->AsEnumType()->AddNameInternal(module_name, name, val, is_export); } void EnumType::AddNameInternal(const string& module_name, const char* name, diff --git a/src/Type.h b/src/Type.h index 8b92bded01..b26800cdd5 100644 --- a/src/Type.h +++ b/src/Type.h @@ -270,13 +270,64 @@ public: void SetName(const std::string& arg_name) { name = arg_name; } const std::string& GetName() const { return name; } - typedef std::map > TypeAliasMap; + struct TypePtrComparer { + bool operator()(const TypePtr& a, const TypePtr& b) const + { return a.get() < b.get(); } + }; + using TypePtrSet = std::set; + using TypeAliasMap = std::map>; + /** + * Returns a mapping of type-name to all other type names declared as + * an alias to it. + */ + static const TypeAliasMap& GetAliasMap() + { return type_aliases; } + + /** + * Returns true if the given type name has any declared aliases + */ + static bool HasAliases(std::string_view type_name) + { return Type::type_aliases.find(type_name) != Type::type_aliases.end(); } + + /** + * Returns the set of all type names declared as an aliases to the given + * type name. A static empty set is returned if there are no aliases. + */ + static const TypePtrSet& Aliases(std::string_view type_name) + { + static TypePtrSet empty; + auto it = Type::type_aliases.find(type_name); + return it == Type::type_aliases.end() ? empty : it->second; + } + + [[deprecated("Remove in v4.1. Use zeek::Type::Aliases() instead.")]] static std::set GetAliases(const std::string& type_name) - { return Type::type_aliases[type_name]; } + { + std::set rval; + for ( const auto& t : Type::type_aliases[type_name] ) + rval.emplace(t.get()); + return rval; + } + /** + * Registers a new type alias. + * @param type_name the name of the type to register a new alias for. + * @param type the associated alias type of *type_name*. + * @return true if the alias is now registered or false if the alias was + * already previously registered. + */ + static bool RegisterAlias(std::string_view type_name, TypePtr type) + { + auto it = Type::type_aliases.find(type_name); + if ( it == Type::type_aliases.end() ) + it = Type::type_aliases.emplace(std::string{type_name}, TypePtrSet{}).first; + return it->second.emplace(std::move(type)).second; + } + + [[deprecated("Remove in v4.1. Use zeek::Type::RegisterAlias().")]] static void AddAlias(const std::string &type_name, Type* type) - { Type::type_aliases[type_name].insert(type); } + { Type::type_aliases[type_name].insert({NewRef{}, type}); } protected: Type() = default; diff --git a/src/Var.cc b/src/Var.cc index e1b9826d56..80c181d948 100644 --- a/src/Var.cc +++ b/src/Var.cc @@ -382,10 +382,10 @@ void add_type(ID* id, TypePtr t, std::unique_ptr> attr) // Clone the type to preserve type name aliasing. tnew = t->ShallowClone(); - Type::AddAlias(new_type_name, tnew.get()); + Type::RegisterAlias(new_type_name, tnew); if ( new_type_name != old_type_name && ! old_type_name.empty() ) - Type::AddAlias(old_type_name, tnew.get()); + Type::RegisterAlias(old_type_name, tnew); tnew->SetName(id->Name()); diff --git a/src/parse.y b/src/parse.y index 5b0f866d04..f5206b752e 100644 --- a/src/parse.y +++ b/src/parse.y @@ -171,7 +171,7 @@ static void parser_redef_enum (zeek::detail::ID *id) static void extend_record(zeek::detail::ID* id, std::unique_ptr fields, std::unique_ptr> attrs) { - std::set types = zeek::Type::GetAliases(id->Name()); + const auto& types = zeek::Type::Aliases(id->Name()); if ( types.empty() ) { @@ -189,7 +189,7 @@ static void extend_record(zeek::detail::ID* id, std::unique_ptrAsRecordType()->AddFields(*fields, add_log_attr); diff --git a/src/zeek.bif b/src/zeek.bif index 87b15c23cd..81981283a9 100644 --- a/src/zeek.bif +++ b/src/zeek.bif @@ -1853,6 +1853,125 @@ function type_name%(t: any%): string return zeek::make_intrusive(s); %} +%%{ +static std::map> build_complete_alias_map() + { + std::map> rval; + + for ( const auto& [name, alias_types] : zeek::Type::GetAliasMap() ) + { + rval[name].emplace(name); + + for ( const auto& alias_type : alias_types ) + { + const auto& alias_name = alias_type->GetName(); + rval[name].emplace(alias_name); + rval[alias_name].emplace(name); + } + } + + for ( const auto& [name, aliases] : rval ) + { + std::map more; + + for ( const auto& alias_name : aliases ) + if ( alias_name != name ) + more.emplace(alias_name, false); + + for ( ; ; ) + { + auto prev_size = more.size(); + + for ( auto& [alias_name, checked] : more ) + { + if ( checked ) continue; + + for ( const auto& alias_alias : rval[alias_name] ) + more.emplace(alias_alias, false); + + checked = true; + } + + if ( prev_size == more.size() ) + break; + } + + for ( const auto& [alias_name, checked] : more ) + rval[name].emplace(alias_name); + } + + return rval; + } +%%} + +## Returns all type name aliases of a value or type. +## +## x: An arbitrary value or type. +## +## Returns: The set of all type name aliases of *x* (or the type of *x* +## if it's a value instead of a type). For primitive values +## and types like :zeek:type:`string` or :zeek:type:`count`, +## this returns an empty set. For types with user-defined +## names like :zeek:type:`record` or :zeek:type:`enum`, the +## returned set contains the original user-defined name for the +## type along with all aliases. For other compound types, like +## :zeek:type:`table`, the returned set is empty unless +## explicitly requesting aliases for a user-defined type alias +## or a value that was explicitly created using a type alias +## (as opposed to originating from an "anonymous" constructor +## or initializer for that compound type). +function type_aliases%(x: any%): string_set + %{ + auto rval = make_intrusive(zeek::id::string_set); + + auto t = x->GetType(); + + if ( t->Tag() == TYPE_TYPE ) + t = t->AsTypeType()->GetType(); + + const auto& tname = t->GetName(); + + if ( tname.empty() ) + return rval; + + static auto all_aliases = build_complete_alias_map(); + auto it = all_aliases.find(tname); + + if ( it == all_aliases.end() ) + return rval; + + for ( const auto& name : it->second ) + rval->Assign(make_intrusive(name), nullptr); + + return rval; + %} + +## Returns all value names associated with an enum type. +## +## et: An enum type. +## +## Returns: All enum value names associated with enum type *et*. +## If *et* is not an enum type, an empty set is returned. +function enum_names%(et: any%): string_set + %{ + auto rval = make_intrusive(zeek::id::string_set); + + if ( et->GetType()->Tag() != TYPE_TYPE ) + return rval; + + const auto& t = et->GetType()->AsTypeType()->GetType(); + + if ( t->Tag() != TYPE_ENUM ) + return rval; + + auto enum_type = t->AsEnumType(); + + for ( const auto& [name, i] : enum_type->Names() ) + rval->Assign(make_intrusive(name), nullptr); + + return rval; + %} + ## Returns: list of command-line arguments (``argv``) used to run Zeek. function zeek_args%(%): string_vec %{ @@ -1983,6 +2102,13 @@ function lookup_ID%(id: string%) : any if ( ! i ) return zeek::make_intrusive(""); + if ( i->IsEnumConst() ) + { + auto et = i->GetType()->AsEnumType(); + auto idx = et->Lookup(detail::GLOBAL_MODULE_NAME, i->Name()); + return et->GetEnumVal(idx); + } + if ( ! i->GetVal() ) return zeek::make_intrusive(""); diff --git a/testing/btest/Baseline/bifs.enum_names/out b/testing/btest/Baseline/bifs.enum_names/out new file mode 100644 index 0000000000..664204b545 --- /dev/null +++ b/testing/btest/Baseline/bifs.enum_names/out @@ -0,0 +1,13 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +{ +RED, +GREEN, +PURPLE, +BLUE +} +{ +RED, +GREEN, +PURPLE, +BLUE +} diff --git a/testing/btest/Baseline/bifs.lookup_ID/out b/testing/btest/Baseline/bifs.lookup_ID/out index 40170b1f7c..a43c4f4b2c 100644 --- a/testing/btest/Baseline/bifs.lookup_ID/out +++ b/testing/btest/Baseline/bifs.lookup_ID/out @@ -1,5 +1,7 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. zeek test +GREEN event() diff --git a/testing/btest/Baseline/bifs.type_aliases/out b/testing/btest/Baseline/bifs.type_aliases/out new file mode 100644 index 0000000000..7f79a62c04 --- /dev/null +++ b/testing/btest/Baseline/bifs.type_aliases/out @@ -0,0 +1,22 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +type aliases for 'RED enum val': ColorAlias Color +type aliases for 'Color enum type': ColorAlias Color +type aliases for 'MyRec val': MyRec MyRecAlias +type aliases for 'MyRecAlias val': MyRec MyRecAlias +type aliases for 'MyRec type': MyRec MyRecAlias +type aliases for 'MyRecalias type': MyRec MyRecAlias +type aliases for 'MyString val': it's just a 'string' +type aliases for 'MyString type': MyString AnotherString +type aliases for 'MyOtherString type': MyOtherString +type aliases for 'AnotherString type': MyString AnotherString +type aliases for 'string literal value': it's just a 'string' +type aliases for 'count literal value': it's just a 'count' +type aliases for 'MyTable value': MyTable2 MyTable3 MyTable MyTable4 +type aliases for 'MyTable2 value': MyTable2 MyTable3 MyTable MyTable4 +type aliases for 'MyTable3 value': MyTable2 MyTable3 MyTable MyTable4 +type aliases for 'MyTable4 value': MyTable2 MyTable3 MyTable MyTable4 +type aliases for 'MyTable type': MyTable2 MyTable3 MyTable MyTable4 +type aliases for 'MyTable2 type': MyTable2 MyTable3 MyTable MyTable4 +type aliases for 'MyTable3 type': MyTable2 MyTable3 MyTable MyTable4 +type aliases for 'MyTable4 type': MyTable2 MyTable3 MyTable MyTable4 +type aliases for 'table value': it's just a 'table[count] of string' diff --git a/testing/btest/bifs/enum_names.zeek b/testing/btest/bifs/enum_names.zeek new file mode 100644 index 0000000000..52ef537664 --- /dev/null +++ b/testing/btest/bifs/enum_names.zeek @@ -0,0 +1,10 @@ +# @TEST-EXEC: zeek -b %INPUT >out +# @TEST-EXEC: btest-diff out + +type Color: enum { RED, GREEN, BLUE }; +type ColorAlias: Color; + +redef enum Color += { PURPLE }; + +print enum_names(Color); +print enum_names(ColorAlias); diff --git a/testing/btest/bifs/lookup_ID.zeek b/testing/btest/bifs/lookup_ID.zeek index 534e678729..12494c43a8 100644 --- a/testing/btest/bifs/lookup_ID.zeek +++ b/testing/btest/bifs/lookup_ID.zeek @@ -4,6 +4,8 @@ global a = "zeek test"; +type Color: enum { RED, GREEN, BLUE }; + event zeek_init() { local b = "local value"; @@ -12,5 +14,6 @@ event zeek_init() print lookup_ID(""); print lookup_ID("xyz"); print lookup_ID("b"); + print lookup_ID("GREEN"); print type_name( lookup_ID("zeek_init") ); } diff --git a/testing/btest/bifs/type_aliases.zeek b/testing/btest/bifs/type_aliases.zeek new file mode 100644 index 0000000000..eb27312844 --- /dev/null +++ b/testing/btest/bifs/type_aliases.zeek @@ -0,0 +1,60 @@ +# @TEST-EXEC: zeek -b %INPUT >out +# @TEST-EXEC: btest-diff out + +type MyRec: record { + a: bool &optional; +}; + +type Color: enum { RED, GREEN, BLUE }; +type ColorAlias: Color; + +type MyRecAlias: MyRec; +type MyString: string; +type MyOtherString: string; +type AnotherString: MyString; +type MyTable: table[count] of string; +type MyTable2: MyTable; +type MyTable3: MyTable2; +type MyTable4: MyTable3; + +function type_alias_list(label: string, x: any): string + { + local rval = fmt("type aliases for '%s':", label); + local aliases = type_aliases(x); + + if ( |aliases| == 0 ) + rval += fmt(" it's just a '%s'", type_name(x)); + else + for ( a in aliases ) + rval += fmt(" %s", a); + + return rval; + } + +print type_alias_list("RED enum val", RED); +print type_alias_list("Color enum type", Color); + +print type_alias_list("MyRec val", MyRec()); +print type_alias_list("MyRecAlias val", MyRecAlias()); + +print type_alias_list("MyRec type", MyRec); +print type_alias_list("MyRecalias type", MyRecAlias); + +local mys: MyString = "hi"; +print type_alias_list("MyString val", mys); +print type_alias_list("MyString type", MyString); +print type_alias_list("MyOtherString type", MyOtherString); +print type_alias_list("AnotherString type", AnotherString); + +print type_alias_list("string literal value", "test"); +print type_alias_list("count literal value", 7); + +print type_alias_list("MyTable value", MyTable([1] = "one", [2] = "two")); +print type_alias_list("MyTable2 value", MyTable2([1] = "one", [2] = "two")); +print type_alias_list("MyTable3 value", MyTable3([1] = "one", [2] = "two")); +print type_alias_list("MyTable4 value", MyTable4([1] = "one", [2] = "two")); +print type_alias_list("MyTable type", MyTable); +print type_alias_list("MyTable2 type", MyTable2); +print type_alias_list("MyTable3 type", MyTable3); +print type_alias_list("MyTable4 type", MyTable4); +print type_alias_list("table value", table([1] = "one", [2] = "two"));