Merge remote-tracking branch 'origin/topic/jsiwek/type-alias-introspection'

* origin/topic/jsiwek/type-alias-introspection:
  Add enum_names() BIF to return names of an enum type's values
  Add type_aliases() BIF for introspecting type-names of types/values
  Change Type::type_aliases map to store IntrusivePtr
  Fix lookup_ID() BIF to return enum values
This commit is contained in:
Jon Siwek 2020-11-12 14:30:32 -08:00
commit aab99b743d
12 changed files with 301 additions and 13 deletions

View file

@ -1348,13 +1348,11 @@ void EnumType::CheckAndAddName(const string& module_name, const char* name,
if ( vals.find(val) == vals.end() )
vals[val] = make_intrusive<EnumVal>(IntrusivePtr{NewRef{}, this}, val);
set<Type*> types = Type::GetAliases(GetName());
set<Type*>::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,

View file

@ -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<std::string, std::set<Type*> > TypeAliasMap;
struct TypePtrComparer {
bool operator()(const TypePtr& a, const TypePtr& b) const
{ return a.get() < b.get(); }
};
using TypePtrSet = std::set<TypePtr, TypePtrComparer>;
using TypeAliasMap = std::map<std::string, TypePtrSet, std::less<>>;
/**
* 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<Type*> GetAliases(const std::string& type_name)
{ return Type::type_aliases[type_name]; }
{
std::set<Type*> 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;

View file

@ -382,10 +382,10 @@ void add_type(ID* id, TypePtr t, std::unique_ptr<std::vector<AttrPtr>> 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());

View file

@ -171,7 +171,7 @@ static void parser_redef_enum (zeek::detail::ID *id)
static void extend_record(zeek::detail::ID* id, std::unique_ptr<zeek::type_decl_list> fields,
std::unique_ptr<std::vector<zeek::detail::AttrPtr>> attrs)
{
std::set<zeek::Type*> 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_ptr<zeek::type_decl_
break;
}
for ( auto t : types )
for ( const auto& t : types )
{
auto error = t->AsRecordType()->AddFields(*fields, add_log_attr);

View file

@ -1853,6 +1853,125 @@ function type_name%(t: any%): string
return zeek::make_intrusive<zeek::StringVal>(s);
%}
%%{
static std::map<std::string, std::set<std::string>> build_complete_alias_map()
{
std::map<std::string, std::set<std::string>> 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<std::string, bool> 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::TableVal>(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<zeek::StringVal>(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::TableVal>(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<zeek::StringVal>(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<zeek::StringVal>("<unknown id>");
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<zeek::StringVal>("<no ID value>");