// See the file "COPYING" in the main distribution directory for copyright. #pragma once #include #include #include #include #include #include #include #include "zeek/Attr.h" #include "zeek/ID.h" #include "zeek/IntrusivePtr.h" #include "zeek/Obj.h" #include "zeek/Traverse.h" #include "zeek/ZeekList.h" namespace zeek { class Val; union ZVal; class EnumVal; class RecordVal; class TableVal; using ValPtr = IntrusivePtr; using EnumValPtr = IntrusivePtr; using TableValPtr = IntrusivePtr; namespace detail { class Attributes; class CompositeHash; class Expr; class ListExpr; class ZAMCompiler; class CPPRuntime; using ExprPtr = IntrusivePtr; using ListExprPtr = IntrusivePtr; // The following tracks how to initialize a given record field. class FieldInit { public: virtual ~FieldInit() {} // Return the initialization value of the field. virtual ZVal Generate() const = 0; // Can initialization of the field be deferred? virtual bool IsDeferrable() const { return true; } // Returns the expression evaluated to initialize the field, if any. // (Used for script optimization.) virtual ExprPtr InitExpr() const; }; } // namespace detail // Zeek types. enum TypeTag : uint8_t { TYPE_VOID, // 0 TYPE_BOOL, // 1 TYPE_INT, // 2 TYPE_COUNT, // 3 TYPE_DOUBLE, // 4 TYPE_TIME, // 5 TYPE_INTERVAL, // 6 TYPE_STRING, // 7 TYPE_PATTERN, // 8 TYPE_ENUM, // 9 TYPE_PORT, // 10 TYPE_ADDR, // 11 TYPE_SUBNET, // 12 TYPE_ANY, // 13 TYPE_TABLE, // 14 TYPE_RECORD, // 15 TYPE_LIST, // 16 TYPE_FUNC, // 17 TYPE_FILE, // 18 TYPE_VECTOR, // 19 TYPE_OPAQUE, // 20 TYPE_TYPE, // 21 TYPE_ERROR // 22 #define NUM_TYPES (int(TYPE_ERROR) + 1) }; // Returns the name of the type. extern const char* type_name(TypeTag t); constexpr bool is_network_order(TypeTag tag) noexcept { return tag == TYPE_PORT; } enum FunctionFlavor : uint8_t { FUNC_FLAVOR_FUNCTION, FUNC_FLAVOR_EVENT, FUNC_FLAVOR_HOOK }; enum InternalTypeTag : uint8_t { TYPE_INTERNAL_VOID, TYPE_INTERNAL_INT, TYPE_INTERNAL_UNSIGNED, TYPE_INTERNAL_DOUBLE, TYPE_INTERNAL_STRING, TYPE_INTERNAL_ADDR, TYPE_INTERNAL_SUBNET, TYPE_INTERNAL_OTHER, TYPE_INTERNAL_ERROR }; constexpr InternalTypeTag to_internal_type_tag(TypeTag tag) noexcept { switch ( tag ) { case TYPE_VOID: return TYPE_INTERNAL_VOID; case TYPE_BOOL: case TYPE_INT: case TYPE_ENUM: return TYPE_INTERNAL_INT; case TYPE_COUNT: case TYPE_PORT: return TYPE_INTERNAL_UNSIGNED; case TYPE_DOUBLE: case TYPE_TIME: case TYPE_INTERVAL: return TYPE_INTERNAL_DOUBLE; case TYPE_STRING: return TYPE_INTERNAL_STRING; case TYPE_ADDR: return TYPE_INTERNAL_ADDR; case TYPE_SUBNET: return TYPE_INTERNAL_SUBNET; case TYPE_PATTERN: case TYPE_ANY: case TYPE_TABLE: case TYPE_RECORD: case TYPE_LIST: case TYPE_FUNC: case TYPE_FILE: case TYPE_OPAQUE: case TYPE_VECTOR: case TYPE_TYPE: return TYPE_INTERNAL_OTHER; case TYPE_ERROR: return TYPE_INTERNAL_ERROR; } /* this should be unreachable */ return TYPE_INTERNAL_VOID; } class Type; class TypeList; class TableType; class SetType; class RecordType; class FuncType; class EnumType; class VectorType; class TypeType; class OpaqueType; class FileType; using TypePtr = IntrusivePtr; using TypeListPtr = IntrusivePtr; using TableTypePtr = IntrusivePtr; using SetTypePtr = IntrusivePtr; using RecordTypePtr = IntrusivePtr; using FuncTypePtr = IntrusivePtr; using EnumTypePtr = IntrusivePtr; using VectorTypePtr = IntrusivePtr; using TypeTypePtr = IntrusivePtr; using OpaqueTypePtr = IntrusivePtr; using FileTypePtr = IntrusivePtr; constexpr int DOES_NOT_MATCH_INDEX = 0; constexpr int MATCHES_INDEX_SCALAR = 1; constexpr int MATCHES_INDEX_VECTOR = 2; class Type : public Obj { public: static inline const TypePtr nil; explicit Type(TypeTag tag, bool base_type = false); // Performs a shallow clone operation of the Zeek type. // This especially means that especially for tables the types // are not recursively cloned; altering one type will in this case // alter one of them. // The main use for this is alias tracking. // Clone operations will mostly be implemented in the derived classes; // in addition cloning will be limited to classes that can be reached by // the script-level. virtual TypePtr ShallowClone(); TypeTag Tag() const { return tag; } InternalTypeTag InternalType() const { return internal_tag; } // Whether it's stored in network order. bool IsNetworkOrder() const { return is_network_order; } // Type-checks the given expression list, returning // MATCHES_INDEX_SCALAR = 1 if it matches this type's index // and produces a scalar result (and promoting its // subexpressions as necessary); MATCHES_INDEX_VECTOR = 2 // if it matches and produces a vector result; and // DOES_NOT_MATCH_INDEX = 0 if it can't match (or the type // is not an indexable type). virtual int MatchesIndex(detail::ListExpr* index) const; // Returns the type yielded by this type. For example, if // this type is a table[string] of port, then returns the "port" // type. Returns nil if this is not an index type. virtual const TypePtr& Yield() const; const TypeList* AsTypeList() const; TypeList* AsTypeList(); const TableType* AsTableType() const; TableType* AsTableType(); [[deprecated("Remove in v9.1. Use AsTableType() instead.")]] const SetType* AsSetType() const; [[deprecated("Remove in v9.1. Use AsTableType() instead.")]] SetType* AsSetType(); const RecordType* AsRecordType() const; RecordType* AsRecordType(); const FuncType* AsFuncType() const; FuncType* AsFuncType(); const FileType* AsFileType() const; FileType* AsFileType(); const EnumType* AsEnumType() const; EnumType* AsEnumType(); const VectorType* AsVectorType() const; VectorType* AsVectorType(); const OpaqueType* AsOpaqueType() const; OpaqueType* AsOpaqueType(); const TypeType* AsTypeType() const; TypeType* AsTypeType(); bool IsSet() const { return tag == TYPE_TABLE && ! Yield(); } bool IsTable() const { return tag == TYPE_TABLE && Yield(); } Type* Ref() { ::zeek::Ref(this); return this; } void Describe(ODesc* d) const override; virtual void DescribeReST(ODesc* d, bool roles_only = false) const; void SetName(const std::string& arg_name) { name = arg_name; } const std::string& GetName() const { return name; } virtual detail::TraversalCode Traverse(detail::TraversalCallback* cb) const; 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; } /** * 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; } protected: virtual void DoDescribe(ODesc* d) const; Type() = default; void SetError(); private: TypeTag tag; InternalTypeTag internal_tag; bool is_network_order; bool base_type; std::string name; static TypeAliasMap type_aliases; }; class TypeList final : public Type { public: explicit TypeList(TypePtr arg_pure_type = nullptr) : Type(TYPE_LIST), pure_type(std::move(arg_pure_type)) {} ~TypeList() override = default; const std::vector& GetTypes() const { return types; } bool IsPure() const { return pure_type != nullptr; } // Returns the underlying pure type, or nil if the list // is not pure or is empty. const TypePtr& GetPureType() const { return pure_type; } // Retrospectively instantiates an underlying pure type, if in // fact each element has the same type. void CheckPure(); // True if all of the types match t, false otherwise. If // is_init is true, then the matching is done in the context // of an initialization. bool AllMatch(const Type* t, bool is_init) const; bool AllMatch(const TypePtr& t, bool is_init) const { return AllMatch(t.get(), is_init); } void Append(TypePtr t); void AppendEvenIfNotPure(TypePtr t); // Resets the list to be empty. void Clear() { types.clear(); } detail::TraversalCode Traverse(detail::TraversalCallback* cb) const override; protected: void DoDescribe(ODesc* d) const override; TypePtr pure_type; std::vector types; }; class IndexType : public Type { public: ~IndexType() override = default; int MatchesIndex(detail::ListExpr* index) const override; const TypeListPtr& GetIndices() const { return indices; } const std::vector& GetIndexTypes() const { return indices->GetTypes(); } const TypePtr& Yield() const override { return yield_type; } void DescribeReST(ODesc* d, bool roles_only = false) const override; // Returns true if this table is solely indexed by subnet. bool IsSubNetIndex() const { return is_subnet_index; } // Returns true if this table has a single index of type pattern. bool IsPatternIndex() const { return is_pattern_index; } detail::TraversalCode Traverse(detail::TraversalCallback* cb) const override; protected: IndexType(TypeTag t, TypeListPtr arg_indices, TypePtr arg_yield_type) : Type(t), indices(std::move(arg_indices)), yield_type(std::move(arg_yield_type)) { // "indices" might be nil if we're deferring construction of the type // for "-O use-C++" initialization. if ( indices ) SetSpecialIndices(); else is_subnet_index = is_pattern_index = false; // placeholders } void SetSpecialIndices() { const auto& types = indices->GetTypes(); is_subnet_index = types.size() == 1 && types[0]->Tag() == TYPE_SUBNET; is_pattern_index = types.size() == 1 && types[0]->Tag() == TYPE_PATTERN; } void DoDescribe(ODesc* d) const override; TypeListPtr indices; TypePtr yield_type; bool is_subnet_index; bool is_pattern_index; }; class TableType : public IndexType { public: TableType(TypeListPtr ind, TypePtr yield); ~TableType() override; /** * Assesses whether an &expire_func attribute's function type is compatible * with this table type. * @param attr the &expire_func attribute to check (this method must not be * called with other type of attributes). * @return true if compatible, false if not */ bool CheckExpireFuncCompatibility(const detail::AttrPtr& attr); TypePtr ShallowClone() override; // Returns true if this table type is "unspecified", which is // what one gets using an empty "set()" or "table()" constructor. bool IsUnspecifiedTable() const; const detail::CompositeHash* GetTableHash() const { return table_hash.get(); } // Called to rebuild the associated hash function when a record type // (that this table type depends on) gets redefined during parsing. void RegenerateHash(); private: bool DoExpireCheck(const detail::AttrPtr& attr); std::unique_ptr table_hash; // Used to prevent repeated error messages. bool reported_error = false; }; class [[deprecated("Remove in v9.1. Use TableType instead.")]] SetType final : public TableType { public: SetType(TypeListPtr ind, detail::ListExprPtr arg_elements); ~SetType() override; TypePtr ShallowClone() override; const detail::ListExprPtr& Elements() const { return elements; } protected: detail::ListExprPtr elements; }; class FuncType final : public Type { public: static inline const FuncTypePtr nil; /** * Prototype is only currently used for events and hooks which declare * multiple signature prototypes that allow users to have handlers * with various argument permutations. */ struct Prototype { bool deprecated; std::string deprecation_msg; RecordTypePtr args; // Maps from parameter index in canonical prototype to // parameter index in this alternate prototype. std::map offsets; }; FuncType(RecordTypePtr args, TypePtr yield, FunctionFlavor f); TypePtr ShallowClone() override; const RecordTypePtr& Params() const { return args; } const TypePtr& Yield() const override { return yield; } void SetYieldType(TypePtr arg_yield) { yield = std::move(arg_yield); } FunctionFlavor Flavor() const { return flavor; } std::string FlavorString() const; // Used to convert a function type to an event or hook type. void ClearYieldType(FunctionFlavor arg_flav) { yield = nullptr; flavor = arg_flav; } int MatchesIndex(detail::ListExpr* index) const override; bool CheckArgs(const TypePList* args, bool is_init = false, bool do_warn = true) const; bool CheckArgs(const std::vector& args, bool is_init = false, bool do_warn = true) const; const TypeListPtr& ParamList() const { return arg_types; } void DescribeReST(ODesc* d, bool roles_only = false) const override; /** * Adds a new event/hook signature allowed for use in handlers. */ void AddPrototype(Prototype s); /** * Returns a prototype signature that matches the desired argument types. */ std::optional FindPrototype(const RecordType& args) const; /** * Returns all allowed function prototypes. */ const std::vector& Prototypes() const { return prototypes; } /** * A single lambda "capture" (outer variable used in a lambda's body). */ class Capture { public: Capture(detail::IDPtr _id, bool _deep_copy); Capture(const Capture&) = default; Capture(Capture&&) = default; Capture& operator=(const Capture&) = default; Capture& operator=(Capture&&) = default; ~Capture() = default; auto& Id() const { return id; } bool IsDeepCopy() const { return deep_copy; } bool IsManaged() const { return is_managed; } // For script optimization: void SetID(detail::IDPtr new_id) { id = std::move(new_id); } private: detail::IDPtr id; bool deep_copy; bool is_managed; }; using CaptureList = std::vector; /** * Sets this function's set of captures. Only valid for lambdas. * * @param captures if non-nil, a list of the lambda's captures */ void SetCaptures(std::optional captures); /** * Returns the captures declared for this function, or nil if none. * * @return a vector giving the captures */ const std::optional& GetCaptures() const { return captures; } /** * Returns whether it's acceptable for a "return" inside the function * to not have an expression (even though the function has a return * type). Used internally for lambdas built for "when" statements. */ bool ExpressionlessReturnOkay() const { return expressionless_return_okay; } /** * Sets whether it's acceptable for a "return" inside the function * to not have an expression (even though the function has a return * type). Used internally for lambdas built for "when" statements. */ void SetExpressionlessReturnOkay(bool is_ok) { expressionless_return_okay = is_ok; } detail::TraversalCode Traverse(detail::TraversalCallback* cb) const override; protected: friend FuncTypePtr make_intrusive(); FuncType() : Type(TYPE_FUNC) { flavor = FUNC_FLAVOR_FUNCTION; } void DoDescribe(ODesc* d) const override; RecordTypePtr args; TypeListPtr arg_types; TypePtr yield; FunctionFlavor flavor; std::vector prototypes; std::optional captures; // if nil then no captures specified // Used for internal lambdas built for "when" statements: bool expressionless_return_okay = false; // Used to prevent repeated error messages. bool reported_error = false; }; class TypeType final : public Type { public: explicit TypeType(TypePtr t) : zeek::Type(TYPE_TYPE), type(std::move(t)) {} TypePtr ShallowClone() override { return make_intrusive(type); } const TypePtr& GetType() const { return type; } template IntrusivePtr GetType() const { return cast_intrusive(type); } detail::TraversalCode Traverse(detail::TraversalCallback* cb) const override; protected: TypePtr type; }; class TypeDecl final { public: TypeDecl() = default; TypeDecl(const char* i, TypePtr t, detail::AttributesPtr attrs = nullptr); TypeDecl(const TypeDecl& other); ~TypeDecl(); TypeDecl& operator=(const TypeDecl& other); const detail::AttrPtr& GetAttr(detail::AttrTag a) const { return attrs ? attrs->Find(a) : detail::Attr::nil; } const detail::Location* GetLocationInfo() const { return &loc; } void DescribeReST(ODesc* d, bool roles_only = false) const; TypePtr type; detail::AttributesPtr attrs; const char* id = nullptr; private: detail::Location loc = detail::GetCurrentLocation(); }; using type_decl_list = PList; class RecordType final : public Type { public: explicit RecordType(type_decl_list* types); TypePtr ShallowClone() override; ~RecordType() override; bool HasField(const char* field) const; /** * Looks up a field by name and returns its type. No check for invalid * field name is performed. */ const TypePtr& GetFieldType(const char* field_name) const { return GetFieldType(FieldOffset(field_name)); } /** * Looks up a field by name and returns its type as cast to @c T. * No check for invalid field name is performed. */ template IntrusivePtr GetFieldType(const char* field_name) const { return cast_intrusive(GetFieldType(field_name)); } /** * Looks up a field by its index and returns its type. No check for * invalid field offset is performed. */ const TypePtr& GetFieldType(int field_index) const { return (*types)[field_index]->type; } /** * Looks up a field by its index and returns its type as cast to @c T. * No check for invalid field offset is performed. */ template IntrusivePtr GetFieldType(int field_index) const { return cast_intrusive((*types)[field_index]->type); } ValPtr FieldDefault(int field) const; // A field's offset is its position in the type_decl_list, // starting at 0. Returns negative if the field doesn't exist. int FieldOffset(const char* field) const; // Given an offset, returns the field's name. const char* FieldName(int field) const; const type_decl_list* Types() const { return types; } type_decl_list* Types() { return types; } // Given an offset, returns the field's TypeDecl. const TypeDecl* FieldDecl(int field) const; TypeDecl* FieldDecl(int field); // Returns flags corresponding to which fields in the record // have types requiring memory management (reference counting). const std::vector& ManagedFields() const { return managed_fields; } int NumFields() const { return num_fields; } int NumOrigFields() const { return num_orig_fields; } /** * Returns a "record_field_table" value for introspection purposes. * @param rv an optional record value, if given the values of * all fields will be provided in the returned table. The table * is initialized with the &ordered attribute such that iterating over * the table will yield entries for fields in the same order as they * are kept in the record type types list. */ TableValPtr GetRecordFieldsVal(const RecordVal* rv = nullptr) const; // Returns null if all is ok, otherwise a pointer to an error message. const char* AddFields(const type_decl_list& types, bool add_log_attr = false); void AddFieldsDirectly(const type_decl_list& types, bool add_log_attr = false); void DescribeReST(ODesc* d, bool roles_only = false) const override; void DescribeFields(ODesc* d, bool func_args = false) const; void DescribeFieldsReST(ODesc* d, bool func_args) const; bool IsFieldDeprecated(int field) const { const TypeDecl* decl = FieldDecl(field); return decl && decl->GetAttr(detail::ATTR_DEPRECATED) != nullptr; } bool FieldHasAttr(int field, detail::AttrTag at) const { const TypeDecl* decl = FieldDecl(field); return decl && decl->GetAttr(at) != nullptr; } std::string GetFieldDeprecationWarning(int field, bool has_check) const; detail::TraversalCode Traverse(detail::TraversalCallback* cb) const override; // Can initialization of record values of this type be deferred? // // When record types contain non-const &default expressions or recursively // contain any nested records that themselves are not deferrable, // initialization can not be deferred, otherwise possible. bool IsDeferrable() const; // Whether values of this record type are equivalent upon initial // creation, or might differ (e.g. due to function calls or changes // to global state) - used for script optimization. bool IdempotentCreation() const { return creation_inits.empty(); } static void InitPostScript(); private: RecordType() { types = nullptr; } void AddField(unsigned int field, const TypeDecl* td); void DoDescribe(ODesc* d) const override; // Field initializations that can be deferred to first access, // beneficial for fields that are separately initialized prior // to first access. Nil pointers mean "skip initializing the field". std::vector> deferred_inits; // Field initializations that need to be done upon record creation, // rather than deferred. These are expressions whose value might // change if computed later. // // Such initializations are uncommon, so we represent them using // pairs. std::vector>> creation_inits; class CreationInitsOptimizer; friend zeek::RecordVal; friend zeek::detail::ZAMCompiler; friend zeek::detail::CPPRuntime; const auto& DeferredInits() const { return deferred_inits; } const auto& CreationInits() const { return creation_inits; } // If we were willing to bound the size of records, then we could // use std::bitset here instead. std::vector managed_fields; // Number of fields in the type. int num_fields = 0; // Number of fields in the type when originally declared. int num_orig_fields = 0; type_decl_list* types = nullptr; std::set field_ids; }; class FileType final : public Type { public: explicit FileType(TypePtr yield_type); TypePtr ShallowClone() override { return make_intrusive(yield); } ~FileType() override; const TypePtr& Yield() const override { return yield; } detail::TraversalCode Traverse(detail::TraversalCallback* cb) const override; protected: void DoDescribe(ODesc* d) const override; TypePtr yield; }; class OpaqueType final : public Type { public: explicit OpaqueType(const std::string& name); TypePtr ShallowClone() override { return make_intrusive(name); } ~OpaqueType() override {}; const std::string& Name() const { return name; } void DescribeReST(ODesc* d, bool roles_only = false) const override; protected: OpaqueType() {} void DoDescribe(ODesc* d) const override; std::string name; }; class EnumType final : public Type { public: using enum_name_list = std::list>; explicit EnumType(const EnumType* e); explicit EnumType(const std::string& arg_name); TypePtr ShallowClone() override; ~EnumType() override; // The value of this name is next internal counter value, starting // with zero. The internal counter is incremented. void AddName(const std::string& module_name, const char* name, bool is_export, detail::Expr* deprecation = nullptr, bool from_redef = false); // The value of this name is set to val. Once a value has been // explicitly assigned using this method, no further names can be // added that aren't likewise explicitly initialized. void AddName(const std::string& module_name, const char* name, zeek_int_t val, bool is_export, detail::Expr* deprecation = nullptr, bool from_redef = false); // -1 indicates not found. Second version is for full names // that already incorporate the module. zeek_int_t Lookup(const std::string& module_name, const char* name) const; zeek_int_t Lookup(const std::string& full_name) const; const char* Lookup(zeek_int_t value) const; // Returns 0 if not found // Returns the list of defined names with their values. The names // will be fully qualified with their module name. enum_name_list Names() const; bool HasRedefs() const { return has_redefs; } void DescribeReST(ODesc* d, bool roles_only = false) const override; const EnumValPtr& GetEnumVal(zeek_int_t i); // Only for use by C++-generated code. Non-protected because we // don't know in advance the names of the functions that will // access it. void AddNameInternal(const std::string& full_name, zeek_int_t val); protected: void AddNameInternal(const std::string& module_name, const char* name, zeek_int_t val, bool is_export); void CheckAndAddName(const std::string& module_name, const char* name, zeek_int_t val, bool is_export, detail::Expr* deprecation = nullptr, bool from_redef = false); void DoDescribe(ODesc* d) const override; std::map names; std::map rev_names; // Whether any of the elements of the enum were added via redef's. bool has_redefs = false; std::unordered_map vals; // The counter is initialized to 0 and incremented on every implicit // auto-increment name that gets added (thus its > 0 if // auto-increment is used). Once an explicit value has been // specified, the counter is set to -1. This way counter can be used // as a flag to prevent mixing of auto-increment and explicit // enumerator specifications. zeek_int_t counter; }; class VectorType final : public Type { public: explicit VectorType(TypePtr t); TypePtr ShallowClone() override; ~VectorType() override; const TypePtr& Yield() const override; int MatchesIndex(detail::ListExpr* index) const override; // Returns true if this table type is "unspecified", which is what one // gets using an empty "vector()" constructor. bool IsUnspecifiedVector() const; void DescribeReST(ODesc* d, bool roles_only = false) const override; detail::TraversalCode Traverse(detail::TraversalCallback* cb) const override; protected: void DoDescribe(ODesc* d) const override; TypePtr yield_type; }; // True if the two types are equivalent. If is_init is true then the test is // done in the context of an initialization. If match_record_field_names is // true then for record types the field names have to match, too. extern bool same_type(const Type& t1, const Type& t2, bool is_init = false, bool match_record_field_names = true); inline bool same_type(const TypePtr& t1, const TypePtr& t2, bool is_init = false, bool match_record_field_names = true) { // If the pointers are identical, the type should be the same type. if ( t1.get() == t2.get() ) return true; return same_type(*t1, *t2, is_init, match_record_field_names); } inline bool same_type(const Type* t1, const Type* t2, bool is_init = false, bool match_record_field_names = true) { // If the pointers are identical, the type should be the same type. if ( t1 == t2 ) return true; return same_type(*t1, *t2, is_init, match_record_field_names); } inline bool same_type(const TypePtr& t1, const Type* t2, bool is_init = false, bool match_record_field_names = true) { // If the pointers are identical, the type should be the same type. if ( t1.get() == t2 ) return true; return same_type(*t1, *t2, is_init, match_record_field_names); } inline bool same_type(const Type* t1, const TypePtr& t2, bool is_init = false, bool match_record_field_names = true) { // If the pointers are identical, the type should be the same type. if ( t1 == t2.get() ) return true; return same_type(*t1, *t2, is_init, match_record_field_names); } // True if the two attribute lists are equivalent. extern bool same_attrs(const detail::Attributes* a1, const detail::Attributes* a2); // Returns true if the record sub_rec can be promoted to the record // super_rec. extern bool record_promotion_compatible(const RecordType* super_rec, const RecordType* sub_rec); // If the given Type is a TypeList with just one element, returns // that element, otherwise returns the type. extern const Type* flatten_type(const Type* t); extern Type* flatten_type(Type* t); // Returns the "maximum" of two type tags, in a type-promotion sense. extern TypeTag max_type(TypeTag t1, TypeTag t2); // Given two types, returns the "merge", in which promotable types // are promoted to the maximum of the two. Returns nil (and generates // an error message) if the types are incompatible. TypePtr merge_types(const TypePtr& t1, const TypePtr& t2); // Given a list of expressions, returns the maximal type consistent across // all of them, or nil if this cannot be done. "Maximal" incorporates // notions of arithmetic coercion, but otherwise requires type-equivalence. TypePtr maximal_type(detail::ListExpr* elements); // Given an expression, infer its type when used for an initialization. TypePtr init_type(const detail::ExprPtr& init); // Returns true if argument is an atomic type. bool is_atomic_type(const Type& t); inline bool is_atomic_type(const Type* t) { return is_atomic_type(*t); } inline bool is_atomic_type(const TypePtr& t) { return is_atomic_type(*t); } // True if the given type tag corresponds to type that can be assigned to. extern bool is_assignable(TypeTag t); inline bool is_assignable(Type* t) { return is_assignable(t->Tag()); } // True if the given type tag corresponds to an integral type. inline bool IsIntegral(TypeTag t) { return (t == TYPE_INT || t == TYPE_COUNT); } // True if the given type tag corresponds to an arithmetic type. inline bool IsArithmetic(TypeTag t) { return (IsIntegral(t) || t == TYPE_DOUBLE); } // True if the given type tag corresponds to a boolean type. inline bool IsBool(TypeTag t) { return (t == TYPE_BOOL); } // True if the given type tag corresponds to an interval type. inline bool IsInterval(TypeTag t) { return (t == TYPE_INTERVAL); } // True if the given type tag corresponds to a record type. inline bool IsRecord(TypeTag t) { return (t == TYPE_RECORD); } // True if the given type tag corresponds to a function type. inline bool IsFunc(TypeTag t) { return (t == TYPE_FUNC); } // True if the given type tag is a vector. inline bool IsVector(TypeTag t) { return (t == TYPE_VECTOR); } // True if the given type tag is a string. inline bool IsString(TypeTag t) { return (t == TYPE_STRING); } // True if the given type is an aggregate. inline bool IsAggr(TypeTag tag) { return tag == TYPE_VECTOR || tag == TYPE_TABLE || tag == TYPE_RECORD; } inline bool IsAggr(const Type* t) { return IsAggr(t->Tag()); } inline bool IsAggr(const TypePtr& t) { return IsAggr(t->Tag()); } // True if the given type is a container. inline bool IsContainer(TypeTag tag) { return tag == TYPE_VECTOR || tag == TYPE_TABLE; } // True if the given type tag corresponds to the error type. inline bool IsErrorType(TypeTag t) { return (t == TYPE_ERROR); } // True if both tags are integral types. inline bool BothIntegral(TypeTag t1, TypeTag t2) { return (IsIntegral(t1) && IsIntegral(t2)); } // True if both tags are arithmetic types. inline bool BothArithmetic(TypeTag t1, TypeTag t2) { return (IsArithmetic(t1) && IsArithmetic(t2)); } // True if either tags is an arithmetic type. inline bool EitherArithmetic(TypeTag t1, TypeTag t2) { return (IsArithmetic(t1) || IsArithmetic(t2)); } // True if both tags are boolean types. inline bool BothBool(TypeTag t1, TypeTag t2) { return (IsBool(t1) && IsBool(t2)); } // True if both tags are interval types. inline bool BothInterval(TypeTag t1, TypeTag t2) { return (IsInterval(t1) && IsInterval(t2)); } // True if both tags are string types. inline bool BothString(TypeTag t1, TypeTag t2) { return (IsString(t1) && IsString(t2)); } // True if either tag is the error type. inline bool EitherError(TypeTag t1, TypeTag t2) { return (IsErrorType(t1) || IsErrorType(t2)); } // Returns the basic (non-parameterized) type with the given type. const TypePtr& base_type(TypeTag tag); // Returns the basic error type. inline const TypePtr& error_type() { return base_type(TYPE_ERROR); } } // namespace zeek extern zeek::OpaqueTypePtr md5_type; extern zeek::OpaqueTypePtr sha1_type; extern zeek::OpaqueTypePtr sha256_type; extern zeek::OpaqueTypePtr entropy_type; extern zeek::OpaqueTypePtr cardinality_type; extern zeek::OpaqueTypePtr topk_type; extern zeek::OpaqueTypePtr bloomfilter_type; extern zeek::OpaqueTypePtr x509_opaque_type; extern zeek::OpaqueTypePtr ocsp_resp_opaque_type; extern zeek::OpaqueTypePtr paraglob_type; extern zeek::OpaqueTypePtr counter_metric_type; extern zeek::OpaqueTypePtr counter_metric_family_type; extern zeek::OpaqueTypePtr gauge_metric_type; extern zeek::OpaqueTypePtr gauge_metric_family_type; extern zeek::OpaqueTypePtr histogram_metric_type; extern zeek::OpaqueTypePtr histogram_metric_family_type;