Spicy: Extend exception hierarchy.

We move the current `TypeMismatch` into a  new `ParameterMismatch`
exception that's derived from a more general `TypeMismatch` now that
can also be used for other, non-parameter mismatches.
This commit is contained in:
Robin Sommer 2024-06-18 12:40:37 +02:00
parent a7f10df4f7
commit 751c35b476
No known key found for this signature in database
GPG key ID: D8187293B3FFE5D0

View file

@ -59,15 +59,22 @@ public:
}; };
/** /**
* Exception thrown by event generation code if there's a type mismatch * Exception thrown if there's a type mismatch between Spicy and Zeek side.
* between the Spicy-side value and what the Zeek event expects.
*/ */
class TypeMismatch : public UsageError { class TypeMismatch : public UsageError {
using UsageError::UsageError;
};
/**
* Exception thrown by event generation code if there's a type mismatch between
* a Spicy-side parameter value and what the Zeek event expects.
*/
class ParameterMismatch : public TypeMismatch {
public: public:
TypeMismatch(const std::string_view& msg, std::string_view location = "") ParameterMismatch(const std::string_view& msg, std::string_view location = "")
: UsageError(hilti::rt::fmt("Event parameter mismatch, %s", msg)) {} : TypeMismatch(hilti::rt::fmt("Event parameter mismatch, %s", msg)) {}
TypeMismatch(const std::string_view& have, const TypePtr& want, std::string_view location = "") ParameterMismatch(const std::string_view& have, const TypePtr& want, std::string_view location = "")
: TypeMismatch(_fmt(have, want)) {} : ParameterMismatch(_fmt(have, want)) {}
private: private:
std::string _fmt(const std::string_view& have, const TypePtr& want) { std::string _fmt(const std::string_view& have, const TypePtr& want) {
@ -538,7 +545,7 @@ inline ValPtr to_val(const hilti::rt::DeferredExpression<T, E>& t, const TypePtr
*/ */
inline ValPtr to_val(const std::string& s, const TypePtr& target) { inline ValPtr to_val(const std::string& s, const TypePtr& target) {
if ( target->Tag() != TYPE_STRING ) if ( target->Tag() != TYPE_STRING )
throw TypeMismatch("string", target); throw ParameterMismatch("string", target);
return make_intrusive<StringVal>(s); return make_intrusive<StringVal>(s);
} }
@ -549,7 +556,7 @@ inline ValPtr to_val(const std::string& s, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Bytes& b, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Bytes& b, const TypePtr& target) {
if ( target->Tag() != TYPE_STRING ) if ( target->Tag() != TYPE_STRING )
throw TypeMismatch("string", target); throw ParameterMismatch("string", target);
return make_intrusive<StringVal>(b.str()); return make_intrusive<StringVal>(b.str());
} }
@ -568,7 +575,7 @@ inline ValPtr to_val(hilti::rt::integer::safe<T> i, const TypePtr& target) {
if ( target->Tag() == TYPE_INT ) if ( target->Tag() == TYPE_INT )
return val_mgr->Int(i); return val_mgr->Int(i);
throw TypeMismatch("uint64", target); throw ParameterMismatch("uint64", target);
} }
else { else {
if ( target->Tag() == TYPE_INT ) if ( target->Tag() == TYPE_INT )
@ -578,10 +585,10 @@ inline ValPtr to_val(hilti::rt::integer::safe<T> i, const TypePtr& target) {
if ( i >= 0 ) if ( i >= 0 )
return val_mgr->Count(i); return val_mgr->Count(i);
else else
throw TypeMismatch("negative int64", target); throw ParameterMismatch("negative int64", target);
} }
throw TypeMismatch("int64", target); throw ParameterMismatch("int64", target);
} }
} }
@ -599,7 +606,7 @@ ValPtr to_val(const hilti::rt::ValueReference<T>& t, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Bool& b, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Bool& b, const TypePtr& target) {
if ( target->Tag() != TYPE_BOOL ) if ( target->Tag() != TYPE_BOOL )
throw TypeMismatch("bool", target); throw ParameterMismatch("bool", target);
return val_mgr->Bool(b); return val_mgr->Bool(b);
} }
@ -610,7 +617,7 @@ inline ValPtr to_val(const hilti::rt::Bool& b, const TypePtr& target) {
*/ */
inline ValPtr to_val(double r, const TypePtr& target) { inline ValPtr to_val(double r, const TypePtr& target) {
if ( target->Tag() != TYPE_DOUBLE ) if ( target->Tag() != TYPE_DOUBLE )
throw TypeMismatch("double", target); throw ParameterMismatch("double", target);
return make_intrusive<DoubleVal>(r); return make_intrusive<DoubleVal>(r);
} }
@ -621,7 +628,7 @@ inline ValPtr to_val(double r, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Address& d, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Address& d, const TypePtr& target) {
if ( target->Tag() != TYPE_ADDR ) if ( target->Tag() != TYPE_ADDR )
throw TypeMismatch("addr", target); throw ParameterMismatch("addr", target);
auto in_addr = d.asInAddr(); auto in_addr = d.asInAddr();
if ( auto v4 = std::get_if<struct in_addr>(&in_addr) ) if ( auto v4 = std::get_if<struct in_addr>(&in_addr) )
@ -638,7 +645,7 @@ inline ValPtr to_val(const hilti::rt::Address& d, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Port& p, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Port& p, const TypePtr& target) {
if ( target->Tag() != TYPE_PORT ) if ( target->Tag() != TYPE_PORT )
throw TypeMismatch("port", target); throw ParameterMismatch("port", target);
switch ( p.protocol().value() ) { switch ( p.protocol().value() ) {
case hilti::rt::Protocol::TCP: return val_mgr->Port(p.port(), ::TransportProto::TRANSPORT_TCP); case hilti::rt::Protocol::TCP: return val_mgr->Port(p.port(), ::TransportProto::TRANSPORT_TCP);
@ -657,7 +664,7 @@ inline ValPtr to_val(const hilti::rt::Port& p, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Interval& i, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Interval& i, const TypePtr& target) {
if ( target->Tag() != TYPE_INTERVAL ) if ( target->Tag() != TYPE_INTERVAL )
throw TypeMismatch("interval", target); throw ParameterMismatch("interval", target);
return make_intrusive<IntervalVal>(i.seconds()); return make_intrusive<IntervalVal>(i.seconds());
} }
@ -668,7 +675,7 @@ inline ValPtr to_val(const hilti::rt::Interval& i, const TypePtr& target) {
*/ */
inline ValPtr to_val(const hilti::rt::Time& t, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Time& t, const TypePtr& target) {
if ( target->Tag() != TYPE_TIME ) if ( target->Tag() != TYPE_TIME )
throw TypeMismatch("time", target); throw ParameterMismatch("time", target);
return make_intrusive<TimeVal>(t.seconds()); return make_intrusive<TimeVal>(t.seconds());
} }
@ -680,7 +687,7 @@ inline ValPtr to_val(const hilti::rt::Time& t, const TypePtr& target) {
template<typename T> template<typename T>
inline ValPtr to_val(const hilti::rt::Vector<T>& v, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Vector<T>& v, const TypePtr& target) {
if ( target->Tag() != TYPE_VECTOR && target->Tag() != TYPE_LIST ) if ( target->Tag() != TYPE_VECTOR && target->Tag() != TYPE_LIST )
throw TypeMismatch("expected vector or list", target); throw ParameterMismatch("expected vector or list", target);
auto vt = cast_intrusive<VectorType>(target); auto vt = cast_intrusive<VectorType>(target);
auto zv = make_intrusive<VectorVal>(vt); auto zv = make_intrusive<VectorVal>(vt);
@ -697,17 +704,17 @@ inline ValPtr to_val(const hilti::rt::Vector<T>& v, const TypePtr& target) {
template<typename K, typename V> template<typename K, typename V>
inline ValPtr to_val(const hilti::rt::Map<K, V>& m, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Map<K, V>& m, const TypePtr& target) {
if constexpr ( hilti::rt::is_tuple<K>::value ) if constexpr ( hilti::rt::is_tuple<K>::value )
throw TypeMismatch("internal error: sets with tuples not yet supported in to_val()"); throw ParameterMismatch("internal error: sets with tuples not yet supported in to_val()");
if ( target->Tag() != TYPE_TABLE ) if ( target->Tag() != TYPE_TABLE )
throw TypeMismatch("map", target); throw ParameterMismatch("map", target);
auto tt = cast_intrusive<TableType>(target); auto tt = cast_intrusive<TableType>(target);
if ( tt->IsSet() ) if ( tt->IsSet() )
throw TypeMismatch("map", target); throw ParameterMismatch("map", target);
if ( tt->GetIndexTypes().size() != 1 ) if ( tt->GetIndexTypes().size() != 1 )
throw TypeMismatch("map with non-tuple elements", target); throw ParameterMismatch("map with non-tuple elements", target);
auto zv = make_intrusive<TableVal>(tt); auto zv = make_intrusive<TableVal>(tt);
@ -727,20 +734,20 @@ inline ValPtr to_val(const hilti::rt::Map<K, V>& m, const TypePtr& target) {
template<typename T> template<typename T>
inline ValPtr to_val(const hilti::rt::Set<T>& s, const TypePtr& target) { inline ValPtr to_val(const hilti::rt::Set<T>& s, const TypePtr& target) {
if ( target->Tag() != TYPE_TABLE ) if ( target->Tag() != TYPE_TABLE )
throw TypeMismatch("set", target); throw ParameterMismatch("set", target);
auto tt = cast_intrusive<TableType>(target); auto tt = cast_intrusive<TableType>(target);
if ( ! tt->IsSet() ) if ( ! tt->IsSet() )
throw TypeMismatch("set", target); throw ParameterMismatch("set", target);
auto zv = make_intrusive<TableVal>(tt); auto zv = make_intrusive<TableVal>(tt);
for ( const auto& i : s ) { for ( const auto& i : s ) {
if constexpr ( hilti::rt::is_tuple<T>::value ) if constexpr ( hilti::rt::is_tuple<T>::value )
throw TypeMismatch("internal error: sets with tuples not yet supported in to_val()"); throw ParameterMismatch("internal error: sets with tuples not yet supported in to_val()");
else { else {
if ( tt->GetIndexTypes().size() != 1 ) if ( tt->GetIndexTypes().size() != 1 )
throw TypeMismatch("set with non-tuple elements", target); throw ParameterMismatch("set with non-tuple elements", target);
auto idx = to_val(i, tt->GetIndexTypes()[0]); auto idx = to_val(i, tt->GetIndexTypes()[0]);
zv->Assign(std::move(idx), nullptr); zv->Assign(std::move(idx), nullptr);
@ -821,7 +828,7 @@ inline void set_record_field(RecordVal* rval, const IntrusivePtr<RecordType>& rt
// Field must be &optional or &default. // Field must be &optional or &default.
if ( auto attrs = rtype->FieldDecl(idx)->attrs; if ( auto attrs = rtype->FieldDecl(idx)->attrs;
! attrs || ! (attrs->Find(detail::ATTR_DEFAULT) || attrs->Find(detail::ATTR_OPTIONAL)) ) ! attrs || ! (attrs->Find(detail::ATTR_DEFAULT) || attrs->Find(detail::ATTR_OPTIONAL)) )
throw TypeMismatch(hilti::rt::fmt("missing initialization for field '%s'", rtype->FieldName(idx))); throw ParameterMismatch(hilti::rt::fmt("missing initialization for field '%s'", rtype->FieldName(idx)));
} }
} }
} }
@ -833,12 +840,12 @@ inline void set_record_field(RecordVal* rval, const IntrusivePtr<RecordType>& rt
template<typename T, typename std::enable_if_t<hilti::rt::is_tuple<T>::value>*> template<typename T, typename std::enable_if_t<hilti::rt::is_tuple<T>::value>*>
inline ValPtr to_val(const T& t, const TypePtr& target) { inline ValPtr to_val(const T& t, const TypePtr& target) {
if ( target->Tag() != TYPE_RECORD ) if ( target->Tag() != TYPE_RECORD )
throw TypeMismatch("tuple", target); throw ParameterMismatch("tuple", target);
auto rtype = cast_intrusive<RecordType>(target); auto rtype = cast_intrusive<RecordType>(target);
if ( std::tuple_size<T>::value != rtype->NumFields() ) if ( std::tuple_size<T>::value != rtype->NumFields() )
throw TypeMismatch("tuple", target); throw ParameterMismatch("tuple", target);
auto rval = make_intrusive<RecordVal>(rtype); auto rval = make_intrusive<RecordVal>(rtype);
size_t idx = 0; size_t idx = 0;
@ -856,12 +863,12 @@ inline ValPtr to_val(const hilti::rt::Bitfield<Ts...>& v, const TypePtr& target)
using Bitfield = hilti::rt::Bitfield<Ts...>; using Bitfield = hilti::rt::Bitfield<Ts...>;
if ( target->Tag() != TYPE_RECORD ) if ( target->Tag() != TYPE_RECORD )
throw TypeMismatch("bitfield", target); throw ParameterMismatch("bitfield", target);
auto rtype = cast_intrusive<RecordType>(target); auto rtype = cast_intrusive<RecordType>(target);
if ( sizeof...(Ts) - 1 != rtype->NumFields() ) if ( sizeof...(Ts) - 1 != rtype->NumFields() )
throw TypeMismatch("bitfield", target); throw ParameterMismatch("bitfield", target);
auto rval = make_intrusive<RecordVal>(rtype); auto rval = make_intrusive<RecordVal>(rtype);
size_t idx = 0; size_t idx = 0;
@ -887,7 +894,7 @@ constexpr bool is_optional = is_optional_impl<std::remove_cv_t<std::remove_refer
template<typename T, typename std::enable_if_t<std::is_base_of<::hilti::rt::trait::isStruct, T>::value>*> template<typename T, typename std::enable_if_t<std::is_base_of<::hilti::rt::trait::isStruct, T>::value>*>
inline ValPtr to_val(const T& t, const TypePtr& target) { inline ValPtr to_val(const T& t, const TypePtr& target) {
if ( target->Tag() != TYPE_RECORD ) if ( target->Tag() != TYPE_RECORD )
throw TypeMismatch("struct", target); throw ParameterMismatch("struct", target);
auto rtype = cast_intrusive<RecordType>(target); auto rtype = cast_intrusive<RecordType>(target);
@ -898,7 +905,7 @@ inline ValPtr to_val(const T& t, const TypePtr& target) {
t.__visit([&](std::string_view name, const auto& val) { t.__visit([&](std::string_view name, const auto& val) {
if ( idx >= num_fields ) if ( idx >= num_fields )
throw TypeMismatch(hilti::rt::fmt("no matching record field for field '%s'", name)); throw ParameterMismatch(hilti::rt::fmt("no matching record field for field '%s'", name));
// Special-case: Lift up anonymous bitfields (which always come as std::optionals). // Special-case: Lift up anonymous bitfields (which always come as std::optionals).
if ( name == "<anon>" ) { if ( name == "<anon>" ) {
@ -924,7 +931,7 @@ inline ValPtr to_val(const T& t, const TypePtr& target) {
std::string field_name = rtype->FieldName(idx); std::string field_name = rtype->FieldName(idx);
if ( field_name != name ) if ( field_name != name )
throw TypeMismatch( throw ParameterMismatch(
hilti::rt::fmt("mismatch in field name: expected '%s', found '%s'", name, field_name)); hilti::rt::fmt("mismatch in field name: expected '%s', found '%s'", name, field_name));
set_record_field(rval.get(), rtype, idx++, val); set_record_field(rval.get(), rtype, idx++, val);
@ -934,7 +941,7 @@ inline ValPtr to_val(const T& t, const TypePtr& target) {
// We already check above that all Spicy-side fields are mapped so we // We already check above that all Spicy-side fields are mapped so we
// can only hit this if there are uninitialized Zeek-side fields left. // can only hit this if there are uninitialized Zeek-side fields left.
if ( idx != num_fields ) if ( idx != num_fields )
throw TypeMismatch(hilti::rt::fmt("missing initialization for field '%s'", rtype->FieldName(idx + 1))); throw ParameterMismatch(hilti::rt::fmt("missing initialization for field '%s'", rtype->FieldName(idx + 1)));
return rval; return rval;
} }
@ -959,7 +966,7 @@ inline ValPtr to_val_for_transport_proto(int64_t val, const TypePtr& target) {
template<typename T, typename std::enable_if_t<std::is_enum<typename T::Value>::value>*> template<typename T, typename std::enable_if_t<std::is_enum<typename T::Value>::value>*>
inline ValPtr to_val(const T& t, const TypePtr& target) { inline ValPtr to_val(const T& t, const TypePtr& target) {
if ( target->Tag() != TYPE_ENUM ) if ( target->Tag() != TYPE_ENUM )
throw TypeMismatch("enum", target); throw ParameterMismatch("enum", target);
// We'll usually be getting an int64_t for T, but allow other signed ints // We'll usually be getting an int64_t for T, but allow other signed ints
// as well. // as well.
@ -969,7 +976,7 @@ inline ValPtr to_val(const T& t, const TypePtr& target) {
// Special case: map enum values to Zeek's semantics. // Special case: map enum values to Zeek's semantics.
if ( target->GetName() == "transport_proto" ) { if ( target->GetName() == "transport_proto" ) {
if ( ! std::is_same_v<T, hilti::rt::Protocol> ) if ( ! std::is_same_v<T, hilti::rt::Protocol> )
throw TypeMismatch(hilti::rt::demangle(typeid(t).name()), target); throw ParameterMismatch(hilti::rt::demangle(typeid(t).name()), target);
return to_val_for_transport_proto(it, target); return to_val_for_transport_proto(it, target);
} }