diff --git a/common/BUILD b/common/BUILD index f5ca6251f..ae802267a 100644 --- a/common/BUILD +++ b/common/BUILD @@ -727,9 +727,10 @@ cc_library( ], exclude = [ "values/*_test.cc", + "values/legacy_list_value.cc", + "values/legacy_map_value.cc", ], ) + [ - "legacy_value.cc", "value.cc", ], hdrs = glob( @@ -740,7 +741,6 @@ cc_library( "values/*_test.h", ], ) + [ - "legacy_value.h", "type_reflector.h", "value.h", ], @@ -748,7 +748,6 @@ cc_library( ":allocator", ":any", ":arena", - ":kind", ":memory", ":native_type", ":optional_ref", @@ -761,13 +760,6 @@ cc_library( "//base:function_result_set", "//common/internal:byte_string", "//common/internal:unknowns", - "//eval/internal:cel_value_equal", - "//eval/public:cel_value", - "//eval/public:message_wrapper", - "//eval/public/structs:cel_proto_wrap_value_to_message", - "//eval/public/structs:legacy_type_info_apis", - "//eval/public/structs:proto_message_type_adapter", - "//eval/public/structs:trivial_legacy_type_info_internal", "//extensions/protobuf/internal:map_reflection", "//extensions/protobuf/internal:qualify", "//internal:casts", @@ -826,6 +818,7 @@ cc_test( "value_test.cc", ], deps = [ + ":legacy_value", ":memory", ":native_type", ":type", @@ -872,9 +865,97 @@ cc_library( deps = ["//base/internal:unknown_set"], ) -alias( +cc_library( name = "legacy_value", - actual = ":value", + srcs = [ + "legacy_value.cc", + "values/legacy_list_value.cc", + "values/legacy_map_value.cc", + ], + hdrs = glob( + [ + "values/*.h", + ], + exclude = [ + "values/*_test.h", + ], + ) + [ + "legacy_value.h", + "type_reflector.h", + "value.h", + ], + deps = [ + ":allocator", + ":any", + ":arena", + ":kind", + ":memory", + ":native_type", + ":optional_ref", + ":type", + ":typeinfo", + ":unknown", + ":value", + ":value_kind", + "//base:attributes", + "//base:function_result", + "//base:function_result_set", + "//common/internal:byte_string", + "//common/internal:unknowns", + "//eval/internal:cel_value_equal", + "//eval/public:cel_value", + "//eval/public:message_wrapper", + "//eval/public/structs:cel_proto_wrap_value_to_message", + "//eval/public/structs:legacy_type_info_apis", + "//eval/public/structs:proto_message_type_adapter", + "//eval/public/structs:trivial_legacy_type_info_internal", + "//extensions/protobuf/internal:map_reflection", + "//extensions/protobuf/internal:qualify", + "//internal:casts", + "//internal:empty_descriptors", + "//internal:json", + "//internal:manual", + "//internal:message_equality", + "//internal:number", + "//internal:protobuf_runtime_version", + "//internal:status_macros", + "//internal:strings", + "//internal:time", + "//internal:utf8", + "//internal:well_known_types", + "//runtime:runtime_options", + "//runtime/internal:errors", + "@com_google_absl//absl/base", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/base:no_destructor", + "@com_google_absl//absl/base:nullability", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:function_ref", + "@com_google_absl//absl/functional:overload", + "@com_google_absl//absl/hash", + "@com_google_absl//absl/log:absl_check", + "@com_google_absl//absl/log:absl_log", + "@com_google_absl//absl/meta:type_traits", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:cord", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:source_location", + "@com_google_absl//absl/types:span", + "@com_google_absl//absl/types:variant", + "@com_google_absl//absl/utility", + "@com_google_protobuf//:any_cc_proto", + "@com_google_protobuf//:duration_cc_proto", + "@com_google_protobuf//:empty_cc_proto", + "@com_google_protobuf//:protobuf", + "@com_google_protobuf//:struct_cc_proto", + "@com_google_protobuf//:timestamp_cc_proto", + "@com_google_protobuf//:wrappers_cc_proto", + "@com_google_protobuf//src/google/protobuf/io", + ], ) cc_library( diff --git a/common/legacy_value.cc b/common/legacy_value.cc index 0ff096ca5..86077d37d 100644 --- a/common/legacy_value.cc +++ b/common/legacy_value.cc @@ -17,12 +17,15 @@ #include #include #include +#include +#include #include #include #include -#include "google/protobuf/struct.pb.h" #include "absl/base/attributes.h" +#include "absl/base/call_once.h" +#include "absl/base/no_destructor.h" #include "absl/base/nullability.h" #include "absl/base/optimization.h" #include "absl/functional/overload.h" @@ -33,19 +36,18 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" -#include "absl/types/span.h" #include "absl/types/variant.h" #include "base/attribute.h" #include "common/kind.h" #include "common/memory.h" +#include "common/native_type.h" #include "common/type.h" #include "common/unknown.h" #include "common/value.h" #include "common/value_kind.h" #include "common/values/legacy_list_value.h" #include "common/values/legacy_map_value.h" -#include "common/values/list_value_builder.h" -#include "common/values/map_value_builder.h" +#include "common/values/legacy_struct_value.h" #include "common/values/values.h" #include "eval/internal/cel_value_equal.h" #include "eval/public/cel_value.h" @@ -54,6 +56,7 @@ #include "eval/public/structs/legacy_type_info_apis.h" #include "eval/public/structs/proto_message_type_adapter.h" #include "eval/public/structs/trivial_legacy_type_info_internal.h" +#include "internal/casts.h" #include "internal/json.h" #include "internal/status_macros.h" #include "runtime/runtime_options.h" @@ -249,6 +252,480 @@ namespace common_internal { namespace { +class EmptyCompatListValueImpl final : public CompatListValue { + public: + static const EmptyCompatListValueImpl& Get() { + static const absl::NoDestructor empty; + return *empty; + } + + EmptyCompatListValueImpl() = default; + + google::protobuf::Arena* GetOwningArena() const override { return nullptr; } + + std::string DebugString() const override { return "[]"; } + + bool IsEmpty() const override { return true; } + + size_t Size() const override { return 0; } + + absl::Status ConvertToJsonArray( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const override { + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(json != nullptr); + ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), + google::protobuf::Descriptor::WELLKNOWNTYPE_LISTVALUE); + + json->Clear(); + return absl::OkStatus(); + } + + CustomListValue Clone(google::protobuf::Arena* absl_nonnull arena) const override { + return CustomListValue(); + } + + int size() const override { return 0; } + + CelValue operator[](int index) const override { + static const absl::NoDestructor error( + absl::InvalidArgumentError("index out of bounds")); + return CelValue::CreateError(&*error); + } + + CelValue Get(google::protobuf::Arena* arena, int index) const override { + if (arena == nullptr) { + return (*this)[index]; + } + return CelValue::CreateError(google::protobuf::Arena::Create( + arena, absl::InvalidArgumentError("index out of bounds"))); + } + + private: + absl::Status Get(size_t index, const google::protobuf::DescriptorPool* absl_nonnull, + google::protobuf::MessageFactory* absl_nonnull, + google::protobuf::Arena* absl_nonnull arena, + Value* absl_nonnull result) const override { + *result = IndexOutOfBoundsError(index, arena); + return absl::OkStatus(); + } +}; + +class CompatListValueImpl final : public CompatListValue { + public: + CompatListValueImpl( + CustomListValue value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) + : value_(std::move(value)), + descriptor_pool_(descriptor_pool), + message_factory_(message_factory), + arena_(arena) { + ABSL_DCHECK(descriptor_pool_ != nullptr); + ABSL_DCHECK(message_factory_ != nullptr); + ABSL_DCHECK(arena_ != nullptr); + } + + google::protobuf::Arena* GetOwningArena() const override { return arena_; } + + std::string DebugString() const override { return value_.DebugString(); } + + absl::Status SerializeTo( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const override { + return value_.SerializeTo(descriptor_pool, message_factory, output); + } + + absl::Status ConvertToJsonArray( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const override { + return value_.ConvertToJsonArray(descriptor_pool, message_factory, json); + } + + absl::Status Equal(const ListValue& other, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, + Value* absl_nonnull result) const override { + return value_.Equal(Value(other), descriptor_pool, message_factory, arena, + result); + } + + bool IsZeroValue() const override { return value_.IsZeroValue(); } + + CustomListValue Clone(google::protobuf::Arena* absl_nonnull arena) const override { + ABSL_DCHECK(arena != nullptr); + if (value_.IsEmpty()) { + return CustomListValue(); + } + return CustomListValue(google::protobuf::Arena::Create( + arena, value_.Clone(arena), descriptor_pool_, + message_factory_, arena), + arena); + } + + bool IsEmpty() const override { return value_.IsEmpty(); } + + size_t Size() const override { return value_.Size(); } + + absl::Status ForEach( + ForEachWithIndexCallback callback, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) const override { + return value_.ForEach(callback, descriptor_pool, message_factory, arena); + } + + absl::StatusOr NewIterator() const override { + return value_.NewIterator(); + } + + absl::Status Contains( + const Value& other, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, + Value* absl_nonnull result) const override { + return value_.Contains(other, descriptor_pool, message_factory, arena, + result); + } + + CelValue operator[](int index) const override { return Get(arena_, index); } + + CelValue Get(google::protobuf::Arena* arena, int index) const override { + if (arena == nullptr) { + arena = arena_; + } + if (ABSL_PREDICT_FALSE(index < 0 || index >= size())) { + return CelValue::CreateError(google::protobuf::Arena::Create( + arena, common_internal::MakeIndexOutOfBoundsError(index))); + } + Value element; + absl::Status status = + value_.Get(static_cast(index), descriptor_pool_, + message_factory_, arena, &element); + if (ABSL_PREDICT_FALSE(!status.ok())) { + return CelValue::CreateError( + google::protobuf::Arena::Create(arena, std::move(status))); + } + return common_internal::UnsafeLegacyValue(element, /*stable=*/false, arena); + } + + int size() const override { return static_cast(Size()); } + + protected: + absl::Status Get(size_t index, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, + Value* absl_nonnull result) const override { + return value_.Get(index, descriptor_pool, message_factory, arena, result); + } + + private: + const CustomListValue value_; + const google::protobuf::DescriptorPool* absl_nonnull const descriptor_pool_; + google::protobuf::MessageFactory* absl_nonnull const message_factory_; + google::protobuf::Arena* absl_nonnull const arena_; +}; + +class EmptyCompatMapValueImpl final : public CompatMapValue { + public: + static const EmptyCompatMapValueImpl& Get() { + static const absl::NoDestructor empty; + return *empty; + } + + EmptyCompatMapValueImpl() = default; + + google::protobuf::Arena* GetOwningArena() const override { return nullptr; } + + std::string DebugString() const override { return "{}"; } + + bool IsEmpty() const override { return true; } + + size_t Size() const override { return 0; } + + absl::Status ListKeys( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, + ListValue* absl_nonnull result) const override { + *result = ListValue(); + return absl::OkStatus(); + } + + absl::StatusOr NewIterator() const override { + return NewEmptyValueIterator(); + } + + absl::Status ConvertToJsonObject( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const override { + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(json != nullptr); + ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), + google::protobuf::Descriptor::WELLKNOWNTYPE_STRUCT); + + json->Clear(); + return absl::OkStatus(); + } + + CustomMapValue Clone(google::protobuf::Arena* absl_nonnull) const override { + return CustomMapValue(); + } + + absl::optional operator[](CelValue key) const override { + return std::nullopt; + } + + using CompatMapValue::Get; + absl::optional Get(google::protobuf::Arena* arena, + CelValue key) const override { + return std::nullopt; + } + + absl::StatusOr Has(const CelValue& key) const override { return false; } + + int size() const override { return static_cast(Size()); } + + absl::StatusOr ListKeys() const override { + return EmptyCompatListValue(); + } + + absl::StatusOr ListKeys(google::protobuf::Arena*) const override { + return ListKeys(); + } + + private: + absl::StatusOr Find( + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, + Value* absl_nonnull result) const override { + return false; + } + + absl::StatusOr Has( + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) const override { + return false; + } +}; + +class CompatMapValueImpl final : public CompatMapValue { + public: + CompatMapValueImpl(CustomMapValue value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) + : value_(std::move(value)), + descriptor_pool_(descriptor_pool), + message_factory_(message_factory), + arena_(arena) { + ABSL_DCHECK(descriptor_pool_ != nullptr); + ABSL_DCHECK(message_factory_ != nullptr); + ABSL_DCHECK(arena_ != nullptr); + } + + google::protobuf::Arena* GetOwningArena() const override { return arena_; } + + std::string DebugString() const override { return value_.DebugString(); } + + absl::Status SerializeTo( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const override { + return value_.SerializeTo(descriptor_pool, message_factory, output); + } + + absl::Status ConvertToJsonObject( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const override { + return value_.ConvertToJsonObject(descriptor_pool, message_factory, json); + } + + absl::Status Equal(const MapValue& other, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, + Value* absl_nonnull result) const override { + return value_.Equal(Value(other), descriptor_pool, message_factory, arena, + result); + } + + bool IsZeroValue() const override { return value_.IsZeroValue(); } + + CustomMapValue Clone(google::protobuf::Arena* absl_nonnull arena) const override { + ABSL_DCHECK(arena != nullptr); + if (value_.IsEmpty()) { + return CustomMapValue(); + } + return CustomMapValue(google::protobuf::Arena::Create( + arena, value_.Clone(arena), descriptor_pool_, + message_factory_, arena), + arena); + } + + bool IsEmpty() const override { return value_.IsEmpty(); } + + size_t Size() const override { return value_.Size(); } + + absl::Status ListKeys( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, + ListValue* absl_nonnull result) const override { + return value_.ListKeys(descriptor_pool, message_factory, arena, result); + } + + absl::Status ForEach( + ForEachCallback callback, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) const override { + return value_.ForEach(callback, descriptor_pool, message_factory, arena); + } + + absl::StatusOr NewIterator() const override { + return value_.NewIterator(); + } + + absl::optional operator[](CelValue key) const override { + return Get(arena_, key); + } + + using CompatMapValue::Get; + absl::optional Get(google::protobuf::Arena* arena, + CelValue key) const override { + if (arena == nullptr) { + arena = arena_; + } + if (auto status = CelValue::CheckMapKeyType(key); !status.ok()) { + status.IgnoreError(); + return std::nullopt; + } + Value modern_key; + if (!ModernValue(arena, key, modern_key).ok()) { + return std::nullopt; + } + Value modern_val; + absl::StatusOr found = value_.Find( + modern_key, descriptor_pool_, message_factory_, arena, &modern_val); + if (!found.ok()) { + return CelValue::CreateError(google::protobuf::Arena::Create( + arena, std::move(found).status())); + } + if (!*found) { + return std::nullopt; + } + return common_internal::UnsafeLegacyValue(modern_val, /*stable=*/false, + arena); + } + + absl::StatusOr Has(const CelValue& key) const override { + CEL_RETURN_IF_ERROR(CelValue::CheckMapKeyType(key)); + Value modern_key; + CEL_RETURN_IF_ERROR(ModernValue(arena_, key, modern_key)); + return Has(modern_key, descriptor_pool_, message_factory_, arena_); + } + + int size() const override { return static_cast(Size()); } + + absl::StatusOr ListKeys() const override { + return ListKeys(arena_); + } + + absl::StatusOr ListKeys(google::protobuf::Arena* arena) const override { + if (arena == nullptr) { + arena = arena_; + } + if (arena == arena_) { + absl::call_once(keys_once_, [this]() { + ListValue modern_keys; + absl::Status status = value_.ListKeys( + descriptor_pool_, message_factory_, arena_, &modern_keys); + if (!status.ok()) { + keys_ = std::move(status); + return; + } + if (auto legacy_list = AsLegacyListValue(modern_keys); + legacy_list.has_value()) { + keys_ = legacy_list->cel_list(); + return; + } + if (auto custom_list = modern_keys.AsCustom(); + custom_list.has_value()) { + keys_ = MakeCompatListValue(*custom_list, descriptor_pool_, + message_factory_, arena_); + return; + } + keys_ = + absl::InternalError("failed to convert list keys to legacy list"); + }); + return keys_; + } + ListValue modern_keys; + CEL_RETURN_IF_ERROR(value_.ListKeys(descriptor_pool_, message_factory_, + arena, &modern_keys)); + if (auto legacy_list = AsLegacyListValue(modern_keys); + legacy_list.has_value()) { + return legacy_list->cel_list(); + } + if (auto custom_list = modern_keys.AsCustom(); custom_list.has_value()) { + return MakeCompatListValue(*custom_list, descriptor_pool_, + message_factory_, arena); + } + return absl::InternalError("failed to convert list keys to legacy list"); + } + + protected: + absl::StatusOr Find( + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, + Value* absl_nonnull result) const override { + return value_.Find(key, descriptor_pool, message_factory, arena, result); + } + + absl::StatusOr Has( + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) const override { + Value result; + CEL_RETURN_IF_ERROR( + value_.Has(key, descriptor_pool, message_factory, arena, &result)); + if (result.IsBool()) { + return result.GetBool().NativeValue(); + } + if (result.IsError()) { + return result.GetError().NativeValue(); + } + return false; + } + + private: + const CustomMapValue value_; + const google::protobuf::DescriptorPool* absl_nonnull const descriptor_pool_; + google::protobuf::MessageFactory* absl_nonnull const message_factory_; + google::protobuf::Arena* absl_nonnull const arena_; + mutable absl::once_flag keys_once_; + mutable absl::StatusOr keys_; +}; + CelValue LegacyTrivialStructValue(google::protobuf::Arena* absl_nonnull arena, const Value& value) { if (auto legacy_struct_value = common_internal::AsLegacyStructValue(value); @@ -367,6 +844,50 @@ LegacyStructValue MakeLegacyStructValue( } // namespace +const CompatListValue* absl_nonnull EmptyCompatListValue() { + return &EmptyCompatListValueImpl::Get(); +} + +absl::StatusOr MakeCompatListValue( + const CustomListValue& value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(arena != nullptr); + if (value.IsEmpty()) { + return EmptyCompatListValue(); + } + if (value.GetTypeId() == NativeTypeId::For()) { + return cel::internal::down_cast(value.interface()); + } + return google::protobuf::Arena::Create( + arena, value, descriptor_pool, message_factory, arena); +} + +const CompatMapValue* absl_nonnull EmptyCompatMapValue() { + return &EmptyCompatMapValueImpl::Get(); +} + +absl::StatusOr MakeCompatMapValue( + const CustomMapValue& value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(arena != nullptr); + if (value.IsEmpty()) { + return EmptyCompatMapValue(); + } + if (NativeTypeId::Of(value) == NativeTypeId::For()) { + return cel::internal::down_cast(value.interface()); + } + return google::protobuf::Arena::Create( + arena, value, descriptor_pool, message_factory, arena); +} + google::api::expr::runtime::CelValue UnsafeLegacyValue( const Value& value, bool stable, google::protobuf::Arena* absl_nonnull arena) { switch (value.kind()) { @@ -407,15 +928,18 @@ google::api::expr::runtime::CelValue UnsafeLegacyValue( } } -std::string LegacyListValue::DebugString() const { - return CelValue::CreateList(impl_).DebugString(); +namespace { + +std::string LegacyListDebugString( + const CelList* absl_nullability_unknown impl) { + return CelValue::CreateList(impl).DebugString(); } -// See `ValueInterface::SerializeTo`. -absl::Status LegacyListValue::SerializeTo( +absl::Status LegacyListSerializeTo( + const CelList* absl_nullability_unknown impl, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const { + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) { ABSL_DCHECK(descriptor_pool != nullptr); ABSL_DCHECK(message_factory != nullptr); ABSL_DCHECK(output != nullptr); @@ -430,7 +954,7 @@ absl::Status LegacyListValue::SerializeTo( google::protobuf::Arena arena; const google::protobuf::Message* wrapped = MaybeWrapValueToMessage( - descriptor, message_factory, CelValue::CreateList(impl_), &arena); + descriptor, message_factory, CelValue::CreateList(impl), &arena); if (wrapped == nullptr) { return absl::UnknownError("failed to convert legacy map to JSON"); } @@ -441,114 +965,110 @@ absl::Status LegacyListValue::SerializeTo( return absl::OkStatus(); } -absl::Status LegacyListValue::ConvertToJson( +absl::Status LegacyListConvertToJson( + const CelList* absl_nullability_unknown impl, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Message* absl_nonnull json) const { - { - ABSL_DCHECK(descriptor_pool != nullptr); - ABSL_DCHECK(message_factory != nullptr); - ABSL_DCHECK(json != nullptr); - ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), - google::protobuf::Descriptor::WELLKNOWNTYPE_VALUE); - - google::protobuf::Arena arena; - const google::protobuf::Message* wrapped = - MaybeWrapValueToMessage(json->GetDescriptor(), message_factory, - CelValue::CreateList(impl_), &arena); - if (wrapped == nullptr) { - return absl::UnknownError("failed to convert legacy list to JSON"); - } + google::protobuf::Message* absl_nonnull json) { + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(json != nullptr); + ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), + google::protobuf::Descriptor::WELLKNOWNTYPE_VALUE); - if (wrapped->GetDescriptor() == json->GetDescriptor()) { - // We can directly use google::protobuf::Message::Copy(). - json->CopyFrom(*wrapped); - } else { - // Equivalent descriptors but not identical. Must serialize and - // deserialize. - absl::Cord serialized; - if (!wrapped->SerializePartialToString(&serialized)) { - return absl::UnknownError(absl::StrCat("failed to serialize message: ", - wrapped->GetTypeName())); - } - if (!json->ParsePartialFromString(serialized)) { - return absl::UnknownError( - absl::StrCat("failed to parsed message: ", json->GetTypeName())); - } + google::protobuf::Arena arena; + const google::protobuf::Message* wrapped = + MaybeWrapValueToMessage(json->GetDescriptor(), message_factory, + CelValue::CreateList(impl), &arena); + if (wrapped == nullptr) { + return absl::UnknownError("failed to convert legacy list to JSON"); + } + + if (wrapped->GetDescriptor() == json->GetDescriptor()) { + json->CopyFrom(*wrapped); + } else { + absl::Cord serialized; + if (!wrapped->SerializePartialToString(&serialized)) { + return absl::UnknownError(absl::StrCat("failed to serialize message: ", + wrapped->GetTypeName())); + } + if (!json->ParsePartialFromString(serialized)) { + return absl::UnknownError( + absl::StrCat("failed to parsed message: ", json->GetTypeName())); } - return absl::OkStatus(); } + return absl::OkStatus(); } -absl::Status LegacyListValue::ConvertToJsonArray( +absl::Status LegacyListConvertToJsonArray( + const CelList* absl_nullability_unknown impl, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Message* absl_nonnull json) const { - { - ABSL_DCHECK(descriptor_pool != nullptr); - ABSL_DCHECK(message_factory != nullptr); - ABSL_DCHECK(json != nullptr); - ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), - google::protobuf::Descriptor::WELLKNOWNTYPE_LISTVALUE); + google::protobuf::Message* absl_nonnull json) { + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(json != nullptr); + ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), + google::protobuf::Descriptor::WELLKNOWNTYPE_LISTVALUE); - google::protobuf::Arena arena; - const google::protobuf::Message* wrapped = - MaybeWrapValueToMessage(json->GetDescriptor(), message_factory, - CelValue::CreateList(impl_), &arena); - if (wrapped == nullptr) { - return absl::UnknownError("failed to convert legacy list to JSON"); - } + google::protobuf::Arena arena; + const google::protobuf::Message* wrapped = + MaybeWrapValueToMessage(json->GetDescriptor(), message_factory, + CelValue::CreateList(impl), &arena); + if (wrapped == nullptr) { + return absl::UnknownError("failed to convert legacy list to JSON"); + } - if (wrapped->GetDescriptor() == json->GetDescriptor()) { - // We can directly use google::protobuf::Message::Copy(). - json->CopyFrom(*wrapped); - } else { - // Equivalent descriptors but not identical. Must serialize and - // deserialize. - absl::Cord serialized; - if (!wrapped->SerializePartialToString(&serialized)) { - return absl::UnknownError(absl::StrCat("failed to serialize message: ", - wrapped->GetTypeName())); - } - if (!json->ParsePartialFromString(serialized)) { - return absl::UnknownError( - absl::StrCat("failed to parsed message: ", json->GetTypeName())); - } + if (wrapped->GetDescriptor() == json->GetDescriptor()) { + json->CopyFrom(*wrapped); + } else { + absl::Cord serialized; + if (!wrapped->SerializePartialToString(&serialized)) { + return absl::UnknownError(absl::StrCat("failed to serialize message: ", + wrapped->GetTypeName())); + } + if (!json->ParsePartialFromString(serialized)) { + return absl::UnknownError( + absl::StrCat("failed to parsed message: ", json->GetTypeName())); } - return absl::OkStatus(); } + return absl::OkStatus(); } -bool LegacyListValue::IsEmpty() const { return impl_->empty(); } +bool LegacyListIsEmpty(const CelList* absl_nullability_unknown impl) { + return impl->empty(); +} -size_t LegacyListValue::Size() const { - return static_cast(impl_->size()); +size_t LegacyListSize(const CelList* absl_nullability_unknown impl) { + return static_cast(impl->size()); } -// See LegacyListValueInterface::Get for documentation. -absl::Status LegacyListValue::Get( - size_t index, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, +absl::Status LegacyListGet( + const CelList* absl_nullability_unknown impl, size_t index, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { - if (ABSL_PREDICT_FALSE(index < 0 || index >= impl_->size())) { + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) { + if (ABSL_PREDICT_FALSE(impl->size() < 0 || + index >= static_cast(impl->size()))) { *result = ErrorValue::From( absl::InvalidArgumentError("index out of bounds"), arena); return absl::OkStatus(); } CEL_RETURN_IF_ERROR( - ModernValue(arena, impl_->Get(arena, static_cast(index)), *result)); + ModernValue(arena, impl->Get(arena, static_cast(index)), *result)); return absl::OkStatus(); } -absl::Status LegacyListValue::ForEach( - ForEachWithIndexCallback callback, +absl::Status LegacyListForEach( + const CelList* absl_nullability_unknown impl, + CustomListValueInterface::ForEachWithIndexCallback callback, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) const { - const auto size = impl_->size(); + google::protobuf::Arena* absl_nonnull arena) { + const auto size = impl->size(); Value element; for (int index = 0; index < size; ++index) { - CEL_RETURN_IF_ERROR(ModernValue(arena, impl_->Get(arena, index), element)); + CEL_RETURN_IF_ERROR(ModernValue(arena, impl->Get(arena, index), element)); CEL_ASSIGN_OR_RETURN(auto ok, callback(index, Value(element))); if (!ok) { break; @@ -557,20 +1077,19 @@ absl::Status LegacyListValue::ForEach( return absl::OkStatus(); } -absl::StatusOr LegacyListValue::NewIterator() - const { - return std::make_unique(impl_); +absl::StatusOr LegacyListNewIterator( + const CelList* absl_nullability_unknown impl) { + return std::make_unique(impl); } -absl::Status LegacyListValue::Contains( - const Value& other, +absl::Status LegacyListContains( + const CelList* absl_nullability_unknown impl, const Value& other, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) { CEL_ASSIGN_OR_RETURN(auto legacy_other, LegacyValue(arena, other)); - const auto* cel_list = impl_; - for (int i = 0; i < cel_list->size(); ++i) { - auto element = cel_list->Get(arena, i); + for (int i = 0; i < impl->size(); ++i) { + auto element = impl->Get(arena, i); absl::optional equal = interop_internal::CelValueEqualImpl(element, legacy_other); // Heterogeneous equality behavior is to just return false if equality @@ -584,14 +1103,28 @@ absl::Status LegacyListValue::Contains( return absl::OkStatus(); } -std::string LegacyMapValue::DebugString() const { - return CelValue::CreateMap(impl_).DebugString(); +constexpr LegacyListValueDispatcher kLegacyListValueDispatcher = { + /*debug_string=*/&LegacyListDebugString, + /*serialize_to=*/&LegacyListSerializeTo, + /*convert_to_json=*/&LegacyListConvertToJson, + /*convert_to_json_array=*/&LegacyListConvertToJsonArray, + /*is_empty=*/&LegacyListIsEmpty, + /*size=*/&LegacyListSize, + /*get=*/&LegacyListGet, + /*for_each=*/&LegacyListForEach, + /*new_iterator=*/&LegacyListNewIterator, + /*contains=*/&LegacyListContains, +}; + +std::string LegacyMapDebugString(const CelMap* absl_nullability_unknown impl) { + return CelValue::CreateMap(impl).DebugString(); } -absl::Status LegacyMapValue::SerializeTo( +absl::Status LegacyMapSerializeTo( + const CelMap* absl_nullability_unknown impl, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const { + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) { ABSL_DCHECK(descriptor_pool != nullptr); ABSL_DCHECK(message_factory != nullptr); ABSL_DCHECK(output != nullptr); @@ -605,7 +1138,7 @@ absl::Status LegacyMapValue::SerializeTo( google::protobuf::Arena arena; const google::protobuf::Message* wrapped = MaybeWrapValueToMessage( - descriptor, message_factory, CelValue::CreateMap(impl_), &arena); + descriptor, message_factory, CelValue::CreateMap(impl), &arena); if (wrapped == nullptr) { return absl::UnknownError("failed to convert legacy map to JSON"); } @@ -616,10 +1149,11 @@ absl::Status LegacyMapValue::SerializeTo( return absl::OkStatus(); } -absl::Status LegacyMapValue::ConvertToJson( +absl::Status LegacyMapConvertToJson( + const CelMap* absl_nullability_unknown impl, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Message* absl_nonnull json) const { + google::protobuf::Message* absl_nonnull json) { ABSL_DCHECK(descriptor_pool != nullptr); ABSL_DCHECK(message_factory != nullptr); ABSL_DCHECK(json != nullptr); @@ -629,16 +1163,14 @@ absl::Status LegacyMapValue::ConvertToJson( google::protobuf::Arena arena; const google::protobuf::Message* wrapped = MaybeWrapValueToMessage(json->GetDescriptor(), message_factory, - CelValue::CreateMap(impl_), &arena); + CelValue::CreateMap(impl), &arena); if (wrapped == nullptr) { return absl::UnknownError("failed to convert legacy map to JSON"); } if (wrapped->GetDescriptor() == json->GetDescriptor()) { - // We can directly use google::protobuf::Message::Copy(). json->CopyFrom(*wrapped); } else { - // Equivalent descriptors but not identical. Must serialize and deserialize. absl::Cord serialized; if (!wrapped->SerializePartialToString(&serialized)) { return absl::UnknownError(absl::StrCat("failed to serialize message: ", @@ -652,10 +1184,11 @@ absl::Status LegacyMapValue::ConvertToJson( return absl::OkStatus(); } -absl::Status LegacyMapValue::ConvertToJsonObject( +absl::Status LegacyMapConvertToJsonObject( + const CelMap* absl_nullability_unknown impl, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Message* absl_nonnull json) const { + google::protobuf::Message* absl_nonnull json) { ABSL_DCHECK(descriptor_pool != nullptr); ABSL_DCHECK(message_factory != nullptr); ABSL_DCHECK(json != nullptr); @@ -665,16 +1198,14 @@ absl::Status LegacyMapValue::ConvertToJsonObject( google::protobuf::Arena arena; const google::protobuf::Message* wrapped = MaybeWrapValueToMessage(json->GetDescriptor(), message_factory, - CelValue::CreateMap(impl_), &arena); + CelValue::CreateMap(impl), &arena); if (wrapped == nullptr) { return absl::UnknownError("failed to convert legacy map to JSON"); } if (wrapped->GetDescriptor() == json->GetDescriptor()) { - // We can directly use google::protobuf::Message::Copy(). json->CopyFrom(*wrapped); } else { - // Equivalent descriptors but not identical. Must serialize and deserialize. absl::Cord serialized; if (!wrapped->SerializePartialToString(&serialized)) { return absl::UnknownError(absl::StrCat("failed to serialize message: ", @@ -688,17 +1219,19 @@ absl::Status LegacyMapValue::ConvertToJsonObject( return absl::OkStatus(); } -bool LegacyMapValue::IsEmpty() const { return impl_->empty(); } +bool LegacyMapIsEmpty(const CelMap* absl_nullability_unknown impl) { + return impl->empty(); +} -size_t LegacyMapValue::Size() const { - return static_cast(impl_->size()); +size_t LegacyMapSize(const CelMap* absl_nullability_unknown impl) { + return static_cast(impl->size()); } -absl::Status LegacyMapValue::Get( - const Value& key, +absl::Status LegacyMapGet( + const CelMap* absl_nullability_unknown impl, const Value& key, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) { switch (key.kind()) { case ValueKind::kError: ABSL_FALLTHROUGH_INTENDED; @@ -718,7 +1251,7 @@ absl::Status LegacyMapValue::Get( return absl::OkStatus(); } CEL_ASSIGN_OR_RETURN(auto cel_key, LegacyValue(arena, key)); - auto cel_value = impl_->Get(arena, cel_key); + auto cel_value = impl->Get(arena, cel_key); if (!cel_value.has_value()) { *result = NoSuchKeyError(key.DebugString(), arena); return absl::OkStatus(); @@ -727,11 +1260,11 @@ absl::Status LegacyMapValue::Get( return absl::OkStatus(); } -absl::StatusOr LegacyMapValue::Find( - const Value& key, +absl::StatusOr LegacyMapFind( + const CelMap* absl_nullability_unknown impl, const Value& key, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) { switch (key.kind()) { case ValueKind::kError: ABSL_FALLTHROUGH_INTENDED; @@ -750,7 +1283,7 @@ absl::StatusOr LegacyMapValue::Find( *result = ErrorValue::From(InvalidMapKeyTypeError(key.kind()), arena); } CEL_ASSIGN_OR_RETURN(auto cel_key, LegacyValue(arena, key)); - auto cel_value = impl_->Get(arena, cel_key); + auto cel_value = impl->Get(arena, cel_key); if (!cel_value.has_value()) { *result = NullValue{}; return false; @@ -759,11 +1292,11 @@ absl::StatusOr LegacyMapValue::Find( return true; } -absl::Status LegacyMapValue::Has( - const Value& key, +absl::Status LegacyMapHas( + const CelMap* absl_nullability_unknown impl, const Value& key, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) { switch (key.kind()) { case ValueKind::kError: ABSL_FALLTHROUGH_INTENDED; @@ -783,7 +1316,7 @@ absl::Status LegacyMapValue::Has( return absl::OkStatus(); } CEL_ASSIGN_OR_RETURN(auto cel_key, LegacyValue(arena, key)); - absl::StatusOr has = impl_->Has(cel_key); + absl::StatusOr has = impl->Has(cel_key); if (!has.ok()) { *result = ErrorValue::From(std::move(has).status(), arena); return absl::OkStatus(); @@ -793,29 +1326,34 @@ absl::Status LegacyMapValue::Has( return absl::OkStatus(); } -absl::Status LegacyMapValue::ListKeys( +absl::Status LegacyMapListKeys( + const CelMap* absl_nullability_unknown impl, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, ListValue* absl_nonnull result) const { - CEL_ASSIGN_OR_RETURN(auto keys, impl_->ListKeys(arena)); + google::protobuf::Arena* absl_nonnull arena, ListValue* absl_nonnull result) { + CEL_ASSIGN_OR_RETURN(auto keys, impl->ListKeys(arena)); *result = ListValue{common_internal::LegacyListValue(keys)}; return absl::OkStatus(); } -absl::Status LegacyMapValue::ForEach( - ForEachCallback callback, +absl::Status LegacyMapForEach( + const CelMap* absl_nullability_unknown impl, + CustomMapValueInterface::ForEachCallback callback, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) const { - CEL_ASSIGN_OR_RETURN(auto keys, impl_->ListKeys(arena)); + google::protobuf::Arena* absl_nonnull arena) { + CEL_ASSIGN_OR_RETURN(auto keys, impl->ListKeys(arena)); const auto size = keys->size(); Value key; Value value; for (int index = 0; index < size; ++index) { auto cel_key = keys->Get(arena, index); - auto cel_value = *impl_->Get(arena, cel_key); + absl::optional cel_value = impl->Get(arena, cel_key); + if (!cel_value.has_value()) { + return absl::NotFoundError("Key from ListKeys not found in CelMap"); + } CEL_RETURN_IF_ERROR(ModernValue(arena, cel_key, key)); - CEL_RETURN_IF_ERROR(ModernValue(arena, cel_value, value)); + CEL_RETURN_IF_ERROR(ModernValue(arena, *cel_value, value)); CEL_ASSIGN_OR_RETURN(auto ok, callback(key, value)); if (!ok) { break; @@ -824,87 +1362,115 @@ absl::Status LegacyMapValue::ForEach( return absl::OkStatus(); } -absl::StatusOr LegacyMapValue::NewIterator() - const { - return std::make_unique(impl_); +absl::StatusOr LegacyMapNewIterator( + const CelMap* absl_nullability_unknown impl) { + return std::make_unique(impl); } -absl::string_view LegacyStructValue::GetTypeName() const { - auto message_wrapper = AsMessageWrapper(message_ptr_, legacy_type_info_); +constexpr LegacyMapValueDispatcher kLegacyMapValueDispatcher = { + /*debug_string=*/&LegacyMapDebugString, + /*serialize_to=*/&LegacyMapSerializeTo, + /*convert_to_json=*/&LegacyMapConvertToJson, + /*convert_to_json_object=*/&LegacyMapConvertToJsonObject, + /*is_empty=*/&LegacyMapIsEmpty, + /*size=*/&LegacyMapSize, + /*get=*/&LegacyMapGet, + /*find=*/&LegacyMapFind, + /*has=*/&LegacyMapHas, + /*list_keys=*/&LegacyMapListKeys, + /*for_each=*/&LegacyMapForEach, + /*new_iterator=*/&LegacyMapNewIterator, +}; + +absl::string_view LegacyStructGetTypeName( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info) { + auto message_wrapper = AsMessageWrapper(message_ptr, legacy_type_info); return message_wrapper.legacy_type_info()->GetTypename(message_wrapper); } -std::string LegacyStructValue::DebugString() const { - auto message_wrapper = AsMessageWrapper(message_ptr_, legacy_type_info_); +std::string LegacyStructDebugString( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info) { + auto message_wrapper = AsMessageWrapper(message_ptr, legacy_type_info); return message_wrapper.legacy_type_info()->DebugString(message_wrapper); } -absl::Status LegacyStructValue::SerializeTo( +absl::Status LegacyStructSerializeTo( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const { + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) { ABSL_DCHECK(descriptor_pool != nullptr); ABSL_DCHECK(message_factory != nullptr); ABSL_DCHECK(output != nullptr); if (ABSL_PREDICT_TRUE( - message_ptr_->SerializePartialToZeroCopyStream(output))) { + message_ptr->SerializePartialToZeroCopyStream(output))) { return absl::OkStatus(); } return absl::UnknownError("failed to serialize protocol buffer message"); } -absl::Status LegacyStructValue::ConvertToJson( +absl::Status LegacyStructConvertToJson( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Message* absl_nonnull json) const { - ABSL_DCHECK(message_ptr_ != nullptr); + google::protobuf::Message* absl_nonnull json) { + ABSL_DCHECK(message_ptr != nullptr); ABSL_DCHECK(descriptor_pool != nullptr); ABSL_DCHECK(message_factory != nullptr); ABSL_DCHECK(json != nullptr); ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), google::protobuf::Descriptor::WELLKNOWNTYPE_VALUE); - return internal::MessageToJson(*message_ptr_, descriptor_pool, - message_factory, json); + return internal::MessageToJson(*message_ptr, descriptor_pool, message_factory, + json); } -absl::Status LegacyStructValue::ConvertToJsonObject( +absl::Status LegacyStructConvertToJsonObject( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Message* absl_nonnull json) const { - ABSL_DCHECK(message_ptr_ != nullptr); + google::protobuf::Message* absl_nonnull json) { + ABSL_DCHECK(message_ptr != nullptr); ABSL_DCHECK(descriptor_pool != nullptr); ABSL_DCHECK(message_factory != nullptr); ABSL_DCHECK(json != nullptr); ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), google::protobuf::Descriptor::WELLKNOWNTYPE_STRUCT); - return internal::MessageToJson(*message_ptr_, descriptor_pool, - message_factory, json); + return internal::MessageToJson(*message_ptr, descriptor_pool, message_factory, + json); } -absl::Status LegacyStructValue::Equal( +absl::Status LegacyStructEqual( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, const Value& other, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { - ABSL_DCHECK(message_ptr_ != nullptr); - if (ABSL_PREDICT_FALSE(legacy_type_info_ == TrivialTypeInfo::GetInstance())) { + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) { + ABSL_DCHECK(message_ptr != nullptr); + if (ABSL_PREDICT_FALSE(legacy_type_info == TrivialTypeInfo::GetInstance())) { return absl::UnimplementedError( - absl::StrCat("legacy access APIs missing for ", GetTypeName())); + absl::StrCat("legacy access APIs missing for ", + LegacyStructGetTypeName(message_ptr, legacy_type_info))); } - auto modern_value = UnsafeParsedMessageValue(message_ptr_); + auto modern_value = UnsafeParsedMessageValue(message_ptr); // Unwrap the rhs if it's a legacy struct so the normal implementation // doesn't hit the fallback abstract struct comparison. if (auto other_legacy = common_internal::AsLegacyStructValue(other); other_legacy) { - if (other_legacy->legacy_type_info_ == TrivialTypeInfo::GetInstance()) { + if (other_legacy->legacy_type_info() == TrivialTypeInfo::GetInstance()) { return absl::UnimplementedError(absl::StrCat( "legacy access APIs missing for ", other_legacy->GetTypeName())); } - auto other_message = UnsafeParsedMessageValue(other_legacy->message_ptr_); + auto other_message = UnsafeParsedMessageValue(other_legacy->message_ptr()); return modern_value.Equal(other_message, descriptor_pool, message_factory, arena, result); } @@ -913,25 +1479,29 @@ absl::Status LegacyStructValue::Equal( result); } -bool LegacyStructValue::IsZeroValue() const { - ABSL_DCHECK(message_ptr_ != nullptr); - if (ABSL_PREDICT_FALSE(legacy_type_info_ == TrivialTypeInfo::GetInstance())) { +bool LegacyStructIsZeroValue( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info) { + ABSL_DCHECK(message_ptr != nullptr); + if (ABSL_PREDICT_FALSE(legacy_type_info == TrivialTypeInfo::GetInstance())) { return false; } - return UnsafeParsedMessageValue(message_ptr_).IsZeroValue(); + return UnsafeParsedMessageValue(message_ptr).IsZeroValue(); } -absl::Status LegacyStructValue::GetFieldByName( +absl::Status LegacyStructGetFieldByName( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, absl::string_view name, ProtoWrapperTypeOptions unboxing_options, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { - if (ABSL_PREDICT_FALSE(legacy_type_info_ == TrivialTypeInfo::GetInstance())) { + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) { + if (ABSL_PREDICT_FALSE(legacy_type_info == TrivialTypeInfo::GetInstance())) { *result = NoSuchFieldError(name, arena); return absl::OkStatus(); } - ParsedMessageValue parsed_message = UnsafeParsedMessageValue(message_ptr_); + ParsedMessageValue parsed_message = UnsafeParsedMessageValue(message_ptr); const auto* descriptor = parsed_message.GetDescriptor(); const auto* field = descriptor->FindFieldByName(name); if (field == nullptr) { @@ -944,60 +1514,72 @@ absl::Status LegacyStructValue::GetFieldByName( } return interop_internal::WrapLegacyMessageField( - message_ptr_, field, unboxing_options, descriptor_pool, message_factory, + message_ptr, field, unboxing_options, descriptor_pool, message_factory, arena, result); } -absl::Status LegacyStructValue::GetFieldByNumber( +absl::Status LegacyStructGetFieldByNumber( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, int64_t number, ProtoWrapperTypeOptions unboxing_options, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) { return absl::UnimplementedError( "access to fields by numbers is not available for legacy structs"); } -absl::StatusOr LegacyStructValue::HasFieldByName( - absl::string_view name) const { - ABSL_DCHECK(message_ptr_ != nullptr); - if (ABSL_PREDICT_FALSE(legacy_type_info_ == TrivialTypeInfo::GetInstance())) { +absl::StatusOr LegacyStructHasFieldByName( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + absl::string_view name) { + ABSL_DCHECK(message_ptr != nullptr); + if (ABSL_PREDICT_FALSE(legacy_type_info == TrivialTypeInfo::GetInstance())) { return common_internal::MakeNoSuchFieldError(name); } - return UnsafeParsedMessageValue(message_ptr_).HasFieldByName(name); + return UnsafeParsedMessageValue(message_ptr).HasFieldByName(name); } -absl::StatusOr LegacyStructValue::HasFieldByNumber(int64_t number) const { - ABSL_DCHECK(message_ptr_ != nullptr); - if (ABSL_PREDICT_FALSE(legacy_type_info_ == TrivialTypeInfo::GetInstance())) { +absl::StatusOr LegacyStructHasFieldByNumber( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + int64_t number) { + ABSL_DCHECK(message_ptr != nullptr); + if (ABSL_PREDICT_FALSE(legacy_type_info == TrivialTypeInfo::GetInstance())) { return common_internal::MakeNoSuchFieldError(absl::StrCat(number)); } - return UnsafeParsedMessageValue(message_ptr_).HasFieldByNumber(number); + return UnsafeParsedMessageValue(message_ptr).HasFieldByNumber(number); } -absl::Status LegacyStructValue::ForEachField( - ForEachFieldCallback callback, +absl::Status LegacyStructForEachField( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + CustomStructValueInterface::ForEachFieldCallback callback, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) const { - ABSL_DCHECK(message_ptr_ != nullptr); - if (ABSL_PREDICT_FALSE(legacy_type_info_ == TrivialTypeInfo::GetInstance())) { + google::protobuf::Arena* absl_nonnull arena) { + ABSL_DCHECK(message_ptr != nullptr); + if (ABSL_PREDICT_FALSE(legacy_type_info == TrivialTypeInfo::GetInstance())) { return absl::UnimplementedError( - absl::StrCat("legacy access APIs missing for ", GetTypeName())); + absl::StrCat("legacy access APIs missing for ", + LegacyStructGetTypeName(message_ptr, legacy_type_info))); } - return UnsafeParsedMessageValue(message_ptr_) + return UnsafeParsedMessageValue(message_ptr) .ForEachField(callback, descriptor_pool, message_factory, arena); } -absl::Status LegacyStructValue::Qualify( +absl::Status LegacyStructQualify( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, absl::Span qualifiers, bool presence_test, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result, - int* absl_nonnull count) const { + int* absl_nonnull count) { if (ABSL_PREDICT_FALSE(qualifiers.empty())) { return absl::InvalidArgumentError("invalid select qualifier path."); } - if (ABSL_PREDICT_FALSE(legacy_type_info_ == TrivialTypeInfo::GetInstance())) { + if (ABSL_PREDICT_FALSE(legacy_type_info == TrivialTypeInfo::GetInstance())) { absl::string_view field_name = absl::visit( absl::Overload( [](const FieldSpecifier& field) -> absl::string_view { @@ -1012,7 +1594,7 @@ absl::Status LegacyStructValue::Qualify( return absl::OkStatus(); } - ParsedMessageValue parsed_message = UnsafeParsedMessageValue(message_ptr_); + ParsedMessageValue parsed_message = UnsafeParsedMessageValue(message_ptr); CEL_RETURN_IF_ERROR(parsed_message.Qualify(qualifiers, presence_test, descriptor_pool, message_factory, arena, result, count)); @@ -1021,6 +1603,57 @@ absl::Status LegacyStructValue::Qualify( return absl::OkStatus(); } +const google::protobuf::Message* absl_nullable LegacyStructGetLegacyMessage( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info) { + if (message_ptr == nullptr || legacy_type_info == nullptr || + legacy_type_info == TrivialTypeInfo::GetInstance()) { + return nullptr; + } + // This should not be possible using the normal public APIs, but possible + // if someone used the MessageWrapper class directly. + if (IsWellKnownMessageType(message_ptr->GetDescriptor())) { + return nullptr; + } + return message_ptr; +} + +constexpr LegacyStructValueDispatcher kLegacyStructValueDispatcher = { + /*get_type_name=*/&LegacyStructGetTypeName, + /*debug_string=*/&LegacyStructDebugString, + /*serialize_to=*/&LegacyStructSerializeTo, + /*convert_to_json=*/&LegacyStructConvertToJson, + /*convert_to_json_object=*/&LegacyStructConvertToJsonObject, + /*equal=*/&LegacyStructEqual, + /*is_zero_value=*/&LegacyStructIsZeroValue, + /*get_field_by_name=*/&LegacyStructGetFieldByName, + /*get_field_by_number=*/&LegacyStructGetFieldByNumber, + /*has_field_by_name=*/&LegacyStructHasFieldByName, + /*has_field_by_number=*/&LegacyStructHasFieldByNumber, + /*for_each_field=*/&LegacyStructForEachField, + /*qualify=*/&LegacyStructQualify, + /*get_legacy_message=*/&LegacyStructGetLegacyMessage, + /*wrap_legacy_message_field=*/&interop_internal::WrapLegacyMessageField, +}; + +} // namespace + +LegacyListValue::LegacyListValue( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl) + : dispatcher_(&kLegacyListValueDispatcher), impl_(impl) {} + +LegacyMapValue::LegacyMapValue( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl) + : dispatcher_(&kLegacyMapValueDispatcher), impl_(impl) {} + +LegacyStructValue::LegacyStructValue( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info) + : dispatcher_(&kLegacyStructValueDispatcher), + message_ptr_(message_ptr), + legacy_type_info_(legacy_type_info) {} + } // namespace common_internal absl::Status ModernValue(google::protobuf::Arena* arena, @@ -1288,6 +1921,12 @@ void WrapLegacyFieldAccessResult(google::protobuf::Arena* absl_nonnull arena, *result = WrapLegacyParsedMapField(result->GetParsedMapField(), arena); } else if (result->IsParsedJsonMap()) { *result = WrapLegacyParsedJsonMap(result->GetParsedJsonMap(), arena); + } else if (result->IsCustomList() && result->GetCustomList().interface() == + CustomListValue().interface()) { + *result = CustomListValue(common_internal::EmptyCompatListValue(), arena); + } else if (result->IsCustomMap() && result->GetCustomMap().interface() == + CustomMapValue().interface()) { + *result = CustomMapValue(common_internal::EmptyCompatMapValue(), arena); } } diff --git a/common/legacy_value.h b/common/legacy_value.h index 8fea22ec0..b241a8b81 100644 --- a/common/legacy_value.h +++ b/common/legacy_value.h @@ -24,12 +24,14 @@ #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "absl/types/span.h" +#include "common/native_type.h" #include "common/value.h" #include "eval/public/cel_value.h" #include "internal/status_macros.h" #include "runtime/runtime_options.h" #include "google/protobuf/arena.h" #include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" namespace cel { @@ -48,6 +50,44 @@ absl::StatusOr LegacyValue( namespace common_internal { +class CompatListValue : public CustomListValueInterface, + public google::api::expr::runtime::CelList { + public: + virtual google::protobuf::Arena* GetOwningArena() const = 0; + + private: + NativeTypeId GetNativeTypeId() const final { + return NativeTypeId::For(); + } +}; + +const CompatListValue* absl_nonnull EmptyCompatListValue(); + +absl::StatusOr MakeCompatListValue( + const CustomListValue& value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena); + +class CompatMapValue : public CustomMapValueInterface, + public google::api::expr::runtime::CelMap { + public: + virtual google::protobuf::Arena* GetOwningArena() const = 0; + + private: + NativeTypeId GetNativeTypeId() const final { + return NativeTypeId::For(); + } +}; + +const CompatMapValue* absl_nonnull EmptyCompatMapValue(); + +absl::StatusOr MakeCompatMapValue( + const CustomMapValue& value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena); + // Convert a `cel::Value` to `google::api::expr::runtime::CelValue`, using // `arena` to make memory allocations if necessary. `stable` indicates whether // `cel::Value` is in a location where it will not be moved, so that inline diff --git a/common/value.cc b/common/value.cc index bc8a3d7a0..571767d0d 100644 --- a/common/value.cc +++ b/common/value.cc @@ -57,6 +57,7 @@ #include "runtime/runtime_options.h" #include "google/protobuf/arena.h" #include "google/protobuf/descriptor.h" +#include "google/protobuf/io/zero_copy_stream.h" #include "google/protobuf/message.h" #undef GetMessage @@ -2378,4 +2379,282 @@ StringValue::StringValue(const BytesValue& other) : StringValue(other.value_) { }))); } +namespace common_internal { + +std::string LegacyListValue::DebugString() const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return "[]"; + } + return dispatcher_->debug_string(impl_); +} + +absl::Status LegacyListValue::SerializeTo( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy list value support is not linked"); + } + return dispatcher_->serialize_to(impl_, descriptor_pool, message_factory, + output); +} + +absl::Status LegacyListValue::ConvertToJson( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy list value support is not linked"); + } + return dispatcher_->convert_to_json(impl_, descriptor_pool, message_factory, + json); +} + +absl::Status LegacyListValue::ConvertToJsonArray( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy list value support is not linked"); + } + return dispatcher_->convert_to_json_array(impl_, descriptor_pool, + message_factory, json); +} + +absl::Status LegacyListValue::Equal( + const Value& other, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy list value support is not linked"); + } + if (auto list_value = other.AsList(); list_value.has_value()) { + return ListValueEqual(*this, *list_value, descriptor_pool, message_factory, + arena, result); + } + *result = FalseValue(); + return absl::OkStatus(); +} + +bool LegacyListValue::IsEmpty() const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return true; + } + return dispatcher_->is_empty(impl_); +} + +size_t LegacyListValue::Size() const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return 0; + } + return dispatcher_->size(impl_); +} + +absl::Status LegacyListValue::Get( + size_t index, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy list value support is not linked"); + } + return dispatcher_->get(impl_, index, descriptor_pool, message_factory, arena, + result); +} + +absl::Status LegacyListValue::ForEach( + ForEachWithIndexCallback callback, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy list value support is not linked"); + } + return dispatcher_->for_each(impl_, callback, descriptor_pool, + message_factory, arena); +} + +absl::StatusOr LegacyListValue::NewIterator() + const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy list value support is not linked"); + } + return dispatcher_->new_iterator(impl_); +} + +absl::Status LegacyListValue::Contains( + const Value& other, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy list value support is not linked"); + } + return dispatcher_->contains(impl_, other, descriptor_pool, message_factory, + arena, result); +} + +std::string LegacyMapValue::DebugString() const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return "{}"; + } + return dispatcher_->debug_string(impl_); +} + +absl::Status LegacyMapValue::SerializeTo( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + return dispatcher_->serialize_to(impl_, descriptor_pool, message_factory, + output); +} + +absl::Status LegacyMapValue::ConvertToJson( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + return dispatcher_->convert_to_json(impl_, descriptor_pool, message_factory, + json); +} + +absl::Status LegacyMapValue::ConvertToJsonObject( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + return dispatcher_->convert_to_json_object(impl_, descriptor_pool, + message_factory, json); +} + +absl::Status LegacyMapValue::Equal( + const Value& other, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + if (auto map_value = other.AsMap(); map_value.has_value()) { + return MapValueEqual(*this, *map_value, descriptor_pool, message_factory, + arena, result); + } + *result = FalseValue(); + return absl::OkStatus(); +} + +bool LegacyMapValue::IsEmpty() const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return true; + } + return dispatcher_->is_empty(impl_); +} + +size_t LegacyMapValue::Size() const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return 0; + } + return dispatcher_->size(impl_); +} + +absl::Status LegacyMapValue::Get( + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + return dispatcher_->get(impl_, key, descriptor_pool, message_factory, arena, + result); +} + +absl::StatusOr LegacyMapValue::Find( + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + return dispatcher_->find(impl_, key, descriptor_pool, message_factory, arena, + result); +} + +absl::Status LegacyMapValue::Has( + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + return dispatcher_->has(impl_, key, descriptor_pool, message_factory, arena, + result); +} + +absl::Status LegacyMapValue::ListKeys( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, ListValue* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + return dispatcher_->list_keys(impl_, descriptor_pool, message_factory, arena, + result); +} + +absl::Status LegacyMapValue::ForEach( + ForEachCallback callback, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + return dispatcher_->for_each(impl_, callback, descriptor_pool, + message_factory, arena); +} + +absl::StatusOr LegacyMapValue::NewIterator() + const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError("legacy map value support is not linked"); + } + return dispatcher_->new_iterator(impl_); +} + +} // namespace common_internal + } // namespace cel diff --git a/common/values/custom_list_value.cc b/common/values/custom_list_value.cc index 5a34b42cf..1e59ab1fe 100644 --- a/common/values/custom_list_value.cc +++ b/common/values/custom_list_value.cc @@ -26,9 +26,7 @@ #include "absl/strings/string_view.h" #include "common/native_type.h" #include "common/value.h" -#include "common/values/list_value_builder.h" #include "common/values/values.h" -#include "eval/public/cel_value.h" #include "internal/status_macros.h" #include "internal/well_known_types.h" #include "google/protobuf/arena.h" @@ -42,9 +40,8 @@ namespace { using ::cel::well_known_types::ListValueReflection; using ::cel::well_known_types::ValueReflection; -using ::google::api::expr::runtime::CelValue; -class EmptyListValue final : public common_internal::CompatListValue { +class EmptyListValue final : public CustomListValueInterface { public: static const EmptyListValue& Get() { static const absl::NoDestructor empty; @@ -53,8 +50,6 @@ class EmptyListValue final : public common_internal::CompatListValue { EmptyListValue() = default; - google::protobuf::Arena* GetOwningArena() const override { return nullptr; } - std::string DebugString() const override { return "[]"; } bool IsEmpty() const override { return true; } @@ -79,22 +74,6 @@ class EmptyListValue final : public common_internal::CompatListValue { return CustomListValue(&EmptyListValue::Get(), arena); } - int size() const override { return 0; } - - CelValue operator[](int index) const override { - static const absl::NoDestructor error( - absl::InvalidArgumentError("index out of bounds")); - return CelValue::CreateError(&*error); - } - - CelValue Get(google::protobuf::Arena* arena, int index) const override { - if (arena == nullptr) { - return (*this)[index]; - } - return CelValue::CreateError(google::protobuf::Arena::Create( - arena, absl::InvalidArgumentError("index out of bounds"))); - } - private: absl::Status Get(size_t index, const google::protobuf::DescriptorPool* absl_nonnull, google::protobuf::MessageFactory* absl_nonnull, @@ -103,18 +82,14 @@ class EmptyListValue final : public common_internal::CompatListValue { *result = IndexOutOfBoundsError(index, arena); return absl::OkStatus(); } + + NativeTypeId GetNativeTypeId() const override { + return NativeTypeId::For(); + } }; } // namespace -namespace common_internal { - -const CompatListValue* absl_nonnull EmptyCompatListValue() { - return &EmptyListValue::Get(); -} - -} // namespace common_internal - class CustomListValueInterfaceIterator final : public ValueIterator { public: explicit CustomListValueInterfaceIterator( diff --git a/common/values/custom_map_value.cc b/common/values/custom_map_value.cc index ce463f890..6e7a91c31 100644 --- a/common/values/custom_map_value.cc +++ b/common/values/custom_map_value.cc @@ -31,10 +31,7 @@ #include "common/native_type.h" #include "common/value.h" #include "common/value_kind.h" -#include "common/values/list_value_builder.h" -#include "common/values/map_value_builder.h" #include "common/values/values.h" -#include "eval/public/cel_value.h" #include "internal/status_macros.h" #include "internal/well_known_types.h" #include "google/protobuf/arena.h" @@ -48,8 +45,6 @@ namespace { using ::cel::well_known_types::StructReflection; using ::cel::well_known_types::ValueReflection; -using ::google::api::expr::runtime::CelList; -using ::google::api::expr::runtime::CelValue; absl::Status NoSuchKeyError(const Value& key) { return absl::NotFoundError( @@ -61,7 +56,7 @@ absl::Status InvalidMapKeyTypeError(ValueKind kind) { absl::StrCat("Invalid map key type: '", ValueKindToString(kind), "'")); } -class EmptyMapValue final : public common_internal::CompatMapValue { +class EmptyMapValue final : public CustomMapValueInterface { public: static const EmptyMapValue& Get() { static const absl::NoDestructor empty; @@ -70,8 +65,6 @@ class EmptyMapValue final : public common_internal::CompatMapValue { EmptyMapValue() = default; - google::protobuf::Arena* GetOwningArena() const override { return nullptr; } - std::string DebugString() const override { return "{}"; } bool IsEmpty() const override { return true; } @@ -109,28 +102,6 @@ class EmptyMapValue final : public common_internal::CompatMapValue { return CustomMapValue(); } - absl::optional operator[](CelValue key) const override { - return std::nullopt; - } - - using CompatMapValue::Get; - absl::optional Get(google::protobuf::Arena* arena, - CelValue key) const override { - return std::nullopt; - } - - absl::StatusOr Has(const CelValue& key) const override { return false; } - - int size() const override { return static_cast(Size()); } - - absl::StatusOr ListKeys() const override { - return common_internal::EmptyCompatListValue(); - } - - absl::StatusOr ListKeys(google::protobuf::Arena*) const override { - return ListKeys(); - } - private: absl::StatusOr Find( const Value& key, @@ -148,18 +119,14 @@ class EmptyMapValue final : public common_internal::CompatMapValue { google::protobuf::Arena* absl_nonnull arena) const override { return false; } + + NativeTypeId GetNativeTypeId() const override { + return NativeTypeId::For(); + } }; } // namespace -namespace common_internal { - -const CompatMapValue* absl_nonnull EmptyCompatMapValue() { - return &EmptyMapValue::Get(); -} - -} // namespace common_internal - class CustomMapValueInterfaceIterator final : public ValueIterator { public: explicit CustomMapValueInterfaceIterator( diff --git a/common/values/legacy_list_value.cc b/common/values/legacy_list_value.cc index 50bf1e6fe..e9676437f 100644 --- a/common/values/legacy_list_value.cc +++ b/common/values/legacy_list_value.cc @@ -27,7 +27,6 @@ #include "common/native_type.h" #include "common/value.h" #include "common/values/legacy_struct_value.h" -#include "common/values/list_value_builder.h" #include "common/values/values.h" #include "eval/public/cel_value.h" #include "internal/casts.h" @@ -324,30 +323,10 @@ ListValue FromLegacyList( } return CustomListValue(); } - if (native_type_id == NativeTypeId::For()) { - const auto* impl = - cel::internal::down_cast(cel_list); - if (google::protobuf::Arena* arena = impl->GetOwningArena(); arena != nullptr) { - return CustomListValue(impl, arena); - } - } } return LegacyListValue(cel_list); } -absl::Status LegacyListValue::Equal( - const Value& other, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { - if (auto list_value = other.AsList(); list_value.has_value()) { - return ListValueEqual(*this, *list_value, descriptor_pool, message_factory, - arena, result); - } - *result = FalseValue(); - return absl::OkStatus(); -} - bool IsLegacyListValue(const Value& value) { return value.variant_.Is(); } @@ -362,17 +341,15 @@ absl::optional AsLegacyListValue(const Value& value) { return GetLegacyListValue(value); } if (auto custom_list_value = value.AsCustomList(); custom_list_value) { + if (custom_list_value->interface() == CustomListValue().interface()) { + return LegacyListValue(EmptyCompatListValue()); + } NativeTypeId native_type_id = custom_list_value->GetTypeId(); if (native_type_id == NativeTypeId::For()) { return LegacyListValue( static_cast( cel::internal::down_cast( custom_list_value->interface()))); - } else if (native_type_id == NativeTypeId::For()) { - return LegacyListValue( - static_cast( - cel::internal::down_cast( - custom_list_value->interface()))); } else if (native_type_id == NativeTypeId::For()) { return LegacyListValue(static_cast< diff --git a/common/values/legacy_list_value.h b/common/values/legacy_list_value.h index 55a169653..0ebe34f39 100644 --- a/common/values/legacy_list_value.h +++ b/common/values/legacy_list_value.h @@ -50,14 +50,76 @@ namespace common_internal { class LegacyListValue; +struct LegacyListValueDispatcher { + using DebugString = std::string (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl); + + using SerializeTo = absl::Status (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output); + + using ConvertToJson = absl::Status (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json); + + using ConvertToJsonArray = absl::Status (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json); + + using IsEmpty = bool (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl); + + using Size = size_t (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl); + + using Get = absl::Status (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl, + size_t index, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result); + + using ForEach = absl::Status (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl, + CustomListValueInterface::ForEachWithIndexCallback callback, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena); + + using NewIterator = absl::StatusOr (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl); + + using Contains = absl::Status (*)( + const google::api::expr::runtime::CelList* absl_nullability_unknown impl, + const Value& other, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result); + + absl_nonnull DebugString debug_string; + absl_nonnull SerializeTo serialize_to; + absl_nonnull ConvertToJson convert_to_json; + absl_nonnull ConvertToJsonArray convert_to_json_array; + absl_nonnull IsEmpty is_empty; + absl_nonnull Size size; + absl_nonnull Get get; + absl_nonnull ForEach for_each; + absl_nonnull NewIterator new_iterator; + absl_nonnull Contains contains; +}; + class LegacyListValue final : private common_internal::ListValueMixin { public: static constexpr ValueKind kKind = ValueKind::kList; explicit LegacyListValue( - const google::api::expr::runtime::CelList* absl_nullability_unknown impl) - : impl_(impl) {} + const google::api::expr::runtime::CelList* absl_nullability_unknown impl); // By default, this creates an empty list whose type is `list(dyn)`. Unless // you can help it, you should use a more specific typed list value. @@ -140,6 +202,7 @@ class LegacyListValue final friend void swap(LegacyListValue& lhs, LegacyListValue& rhs) noexcept { using std::swap; + swap(lhs.dispatcher_, rhs.dispatcher_); swap(lhs.impl_, rhs.impl_); } @@ -147,6 +210,7 @@ class LegacyListValue final friend class common_internal::ValueMixin; friend class common_internal::ListValueMixin; + const LegacyListValueDispatcher* absl_nullable dispatcher_ = nullptr; const google::api::expr::runtime::CelList* absl_nullability_unknown impl_ = nullptr; }; diff --git a/common/values/legacy_map_value.cc b/common/values/legacy_map_value.cc index bd025c51b..0b5bac765 100644 --- a/common/values/legacy_map_value.cc +++ b/common/values/legacy_map_value.cc @@ -24,6 +24,7 @@ #include "absl/base/nullability.h" #include "absl/log/absl_check.h" #include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "common/legacy_value.h" @@ -33,7 +34,6 @@ #include "common/value_kind.h" #include "common/values/legacy_list_value.h" #include "common/values/legacy_struct_value.h" -#include "common/values/map_value_builder.h" #include "common/values/values.h" #include "eval/public/cel_value.h" #include "eval/public/structs/proto_message_type_adapter.h" @@ -88,6 +88,22 @@ absl::Status InvalidMapKeyType(absl::string_view key_type) { absl::StrCat("Invalid map key type: '", key_type, "'")); } +absl::StatusOr ToLegacyListKeys( + const ListValue& keys, google::protobuf::Arena* absl_nonnull arena) { + if (auto legacy_list = AsLegacyListValue(keys); legacy_list.has_value()) { + return legacy_list->cel_list(); + } + if (auto custom_list = keys.AsCustom(); custom_list.has_value()) { + CEL_ASSIGN_OR_RETURN( + const CompatListValue* compat_list, + MakeCompatListValue( + *custom_list, google::protobuf::DescriptorPool::generated_pool(), + google::protobuf::MessageFactory::generated_factory(), arena)); + return compat_list; + } + return absl::InternalError("failed to convert list keys to legacy list"); +} + } // namespace class LegacyParsedMapFieldMapValue final @@ -273,11 +289,7 @@ class LegacyParsedMapFieldMapValue final CEL_RETURN_IF_ERROR(value_.ListKeys( google::protobuf::DescriptorPool::generated_pool(), google::protobuf::MessageFactory::generated_factory(), arena, &keys)); - auto legacy_list = AsLegacyListValue(keys); - if (!legacy_list.has_value()) { - return absl::InternalError("failed to convert list keys to legacy list"); - } - return legacy_list->cel_list(); + return ToLegacyListKeys(keys, arena); } google::protobuf::Arena* absl_nonnull arena() const { return arena_; } @@ -461,11 +473,7 @@ class LegacyParsedJsonMapValue final CEL_RETURN_IF_ERROR(value_.ListKeys( google::protobuf::DescriptorPool::generated_pool(), google::protobuf::MessageFactory::generated_factory(), arena, &keys)); - auto legacy_list = AsLegacyListValue(keys); - if (!legacy_list.has_value()) { - return absl::InternalError("failed to convert list keys to legacy list"); - } - return legacy_list->cel_list(); + return ToLegacyListKeys(keys, arena); } google::protobuf::Arena* absl_nonnull arena() const { return arena_; } @@ -516,30 +524,10 @@ MapValue FromLegacyMap( } return CustomMapValue(); } - if (native_type_id == NativeTypeId::For()) { - const auto* impl = - cel::internal::down_cast(cel_map); - if (google::protobuf::Arena* arena = impl->GetOwningArena(); arena != nullptr) { - return CustomMapValue(impl, arena); - } - } } return LegacyMapValue(cel_map); } -absl::Status LegacyMapValue::Equal( - const Value& other, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { - if (auto map_value = other.AsMap(); map_value.has_value()) { - return MapValueEqual(*this, *map_value, descriptor_pool, message_factory, - arena, result); - } - *result = FalseValue(); - return absl::OkStatus(); -} - bool IsLegacyMapValue(const Value& value) { return value.variant_.Is(); } @@ -554,17 +542,15 @@ std::optional AsLegacyMapValue(const Value& value) { return GetLegacyMapValue(value); } if (auto custom_map_value = value.AsCustomMap(); custom_map_value) { + if (custom_map_value->interface() == CustomMapValue().interface()) { + return LegacyMapValue(EmptyCompatMapValue()); + } NativeTypeId native_type_id = NativeTypeId::Of(*custom_map_value); if (native_type_id == NativeTypeId::For()) { return LegacyMapValue( static_cast( cel::internal::down_cast( custom_map_value->interface()))); - } else if (native_type_id == NativeTypeId::For()) { - return LegacyMapValue( - static_cast( - cel::internal::down_cast( - custom_map_value->interface()))); } else if (native_type_id == NativeTypeId::For()) { return LegacyMapValue( diff --git a/common/values/legacy_map_value.h b/common/values/legacy_map_value.h index c170dea80..04849fdc1 100644 --- a/common/values/legacy_map_value.h +++ b/common/values/legacy_map_value.h @@ -49,14 +49,92 @@ namespace common_internal { class LegacyMapValue; +struct LegacyMapValueDispatcher { + using DebugString = std::string (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl); + + using SerializeTo = absl::Status (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output); + + using ConvertToJson = absl::Status (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json); + + using ConvertToJsonObject = absl::Status (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json); + + using IsEmpty = bool (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl); + + using Size = size_t (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl); + + using Get = absl::Status (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl, + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result); + + using Find = absl::StatusOr (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl, + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result); + + using Has = absl::Status (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl, + const Value& key, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result); + + using ListKeys = absl::Status (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, ListValue* absl_nonnull result); + + using ForEach = absl::Status (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl, + CustomMapValueInterface::ForEachCallback callback, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena); + + using NewIterator = absl::StatusOr (*)( + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl); + + absl_nonnull DebugString debug_string; + absl_nonnull SerializeTo serialize_to; + absl_nonnull ConvertToJson convert_to_json; + absl_nonnull ConvertToJsonObject convert_to_json_object; + absl_nonnull IsEmpty is_empty; + absl_nonnull Size size; + absl_nonnull Get get; + absl_nonnull Find find; + absl_nonnull Has has; + absl_nonnull ListKeys list_keys; + absl_nonnull ForEach for_each; + absl_nonnull NewIterator new_iterator; +}; + class LegacyMapValue final : private common_internal::MapValueMixin { public: static constexpr ValueKind kKind = ValueKind::kMap; explicit LegacyMapValue( - const google::api::expr::runtime::CelMap* absl_nullability_unknown impl) - : impl_(impl) {} + const google::api::expr::runtime::CelMap* absl_nullability_unknown impl); // By default, this creates an empty map whose type is `map(dyn, dyn)`. // Unless you can help it, you should use a more specific typed map value. @@ -158,6 +236,7 @@ class LegacyMapValue final friend void swap(LegacyMapValue& lhs, LegacyMapValue& rhs) noexcept { using std::swap; + swap(lhs.dispatcher_, rhs.dispatcher_); swap(lhs.impl_, rhs.impl_); } @@ -165,6 +244,7 @@ class LegacyMapValue final friend class common_internal::ValueMixin; friend class common_internal::MapValueMixin; + const LegacyMapValueDispatcher* absl_nullable dispatcher_ = nullptr; const google::api::expr::runtime::CelMap* absl_nullability_unknown impl_ = nullptr; }; diff --git a/common/values/legacy_struct_value.cc b/common/values/legacy_struct_value.cc index aa342ad06..088a40ee0 100644 --- a/common/values/legacy_struct_value.cc +++ b/common/values/legacy_struct_value.cc @@ -12,18 +12,218 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include + +#include "absl/base/nullability.h" +#include "absl/base/optimization.h" #include "absl/log/absl_check.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "absl/types/span.h" +#include "base/attribute.h" #include "common/type.h" #include "common/value.h" +#include "runtime/runtime_options.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/io/zero_copy_stream.h" #include "google/protobuf/message.h" namespace cel::common_internal { StructType LegacyStructValue::GetRuntimeType() const { + if (ABSL_PREDICT_FALSE(message_ptr_ == nullptr)) { + ABSL_DCHECK(message_ptr_ != nullptr); + return StructType(); + } return MessageType(message_ptr_->GetDescriptor()); } +absl::string_view LegacyStructValue::GetTypeName() const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return ""; + } + return dispatcher_->get_type_name(message_ptr_, legacy_type_info_); +} + +std::string LegacyStructValue::DebugString() const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return ""; + } + return dispatcher_->debug_string(message_ptr_, legacy_type_info_); +} + +absl::Status LegacyStructValue::SerializeTo( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->serialize_to(message_ptr_, legacy_type_info_, + descriptor_pool, message_factory, output); +} + +absl::Status LegacyStructValue::ConvertToJson( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->convert_to_json(message_ptr_, legacy_type_info_, + descriptor_pool, message_factory, json); +} + +absl::Status LegacyStructValue::ConvertToJsonObject( + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->convert_to_json_object( + message_ptr_, legacy_type_info_, descriptor_pool, message_factory, json); +} + +absl::Status LegacyStructValue::Equal( + const Value& other, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->equal(message_ptr_, legacy_type_info_, other, + descriptor_pool, message_factory, arena, result); +} + +bool LegacyStructValue::IsZeroValue() const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return false; + } + return dispatcher_->is_zero_value(message_ptr_, legacy_type_info_); +} + +absl::Status LegacyStructValue::GetFieldByName( + absl::string_view name, ProtoWrapperTypeOptions unboxing_options, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->get_field_by_name(message_ptr_, legacy_type_info_, name, + unboxing_options, descriptor_pool, + message_factory, arena, result); +} + +absl::Status LegacyStructValue::GetFieldByNumber( + int64_t number, ProtoWrapperTypeOptions unboxing_options, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->get_field_by_number( + message_ptr_, legacy_type_info_, number, unboxing_options, + descriptor_pool, message_factory, arena, result); +} + +absl::StatusOr LegacyStructValue::HasFieldByName( + absl::string_view name) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->has_field_by_name(message_ptr_, legacy_type_info_, name); +} + +absl::StatusOr LegacyStructValue::HasFieldByNumber(int64_t number) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->has_field_by_number(message_ptr_, legacy_type_info_, + number); +} + +absl::Status LegacyStructValue::ForEachField( + ForEachFieldCallback callback, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->for_each_field(message_ptr_, legacy_type_info_, callback, + descriptor_pool, message_factory, arena); +} + +absl::Status LegacyStructValue::Qualify( + absl::Span qualifiers, bool presence_test, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result, + int* absl_nonnull count) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->qualify(message_ptr_, legacy_type_info_, qualifiers, + presence_test, descriptor_pool, message_factory, + arena, result, count); +} + +const google::protobuf::Message* absl_nullable LegacyStructValue::GetLegacyMessage() + const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return nullptr; + } + return dispatcher_->get_legacy_message(message_ptr_, legacy_type_info_); +} + +absl::Status LegacyStructValue::WrapLegacyMessageField( + const google::protobuf::FieldDescriptor* absl_nonnull field_descriptor, + ProtoWrapperTypeOptions unboxing_option, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull out) const { + if (ABSL_PREDICT_FALSE(dispatcher_ == nullptr)) { + ABSL_DCHECK(dispatcher_ != nullptr); + return absl::UnimplementedError( + "legacy struct value support is not linked"); + } + return dispatcher_->wrap_legacy_message_field( + message_ptr_, field_descriptor, unboxing_option, descriptor_pool, + message_factory, arena, out); +} + bool IsLegacyStructValue(const Value& value) { return value.variant_.Is(); } @@ -40,4 +240,11 @@ absl::optional AsLegacyStructValue(const Value& value) { return std::nullopt; } +const google::protobuf::Message* absl_nullable GetLegacyMessage(const Value& value) { + if (auto legacy = AsLegacyStructValue(value); legacy.has_value()) { + return legacy->GetLegacyMessage(); + } + return nullptr; +} + } // namespace cel::common_internal diff --git a/common/values/legacy_struct_value.h b/common/values/legacy_struct_value.h index ab5baed1e..59b5a877f 100644 --- a/common/values/legacy_struct_value.h +++ b/common/values/legacy_struct_value.h @@ -53,6 +53,134 @@ namespace common_internal { class LegacyStructValue; +struct LegacyStructValueDispatcher { + using GetTypeName = absl::string_view (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info); + + using DebugString = std::string (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info); + + using SerializeTo = absl::Status (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::io::ZeroCopyOutputStream* absl_nonnull output); + + using ConvertToJson = absl::Status (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json); + + using ConvertToJsonObject = absl::Status (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Message* absl_nonnull json); + + using Equal = absl::Status (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + const Value& other, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result); + + using IsZeroValue = bool (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info); + + using GetFieldByName = absl::Status (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + absl::string_view name, ProtoWrapperTypeOptions unboxing_options, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result); + + using GetFieldByNumber = absl::Status (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + int64_t number, ProtoWrapperTypeOptions unboxing_options, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result); + + using HasFieldByName = absl::StatusOr (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + absl::string_view name); + + using HasFieldByNumber = absl::StatusOr (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + int64_t number); + + using ForEachField = absl::Status (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + CustomStructValueInterface::ForEachFieldCallback callback, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena); + + using Qualify = absl::Status (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info, + absl::Span qualifiers, bool presence_test, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull result, + int* absl_nonnull count); + + using GetLegacyMessage = const google::protobuf::Message* absl_nullable (*)( + const google::protobuf::Message* absl_nullability_unknown message_ptr, + const google::api::expr::runtime:: + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info); + + using WrapLegacyMessageField = absl::Status (*)( + const google::protobuf::Message* absl_nonnull message, + const google::protobuf::FieldDescriptor* absl_nonnull field_descriptor, + ProtoWrapperTypeOptions unboxing_option, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull out); + + absl_nonnull GetTypeName get_type_name; + absl_nonnull DebugString debug_string; + absl_nonnull SerializeTo serialize_to; + absl_nonnull ConvertToJson convert_to_json; + absl_nonnull ConvertToJsonObject convert_to_json_object; + absl_nonnull Equal equal; + absl_nonnull IsZeroValue is_zero_value; + absl_nonnull GetFieldByName get_field_by_name; + absl_nonnull GetFieldByNumber get_field_by_number; + absl_nonnull HasFieldByName has_field_by_name; + absl_nonnull HasFieldByNumber has_field_by_number; + absl_nonnull ForEachField for_each_field; + absl_nonnull Qualify qualify; + absl_nonnull GetLegacyMessage get_legacy_message; + absl_nonnull WrapLegacyMessageField wrap_legacy_message_field; +}; + // `LegacyStructValue` is a wrapper around the old representation of protocol // buffer messages in `google::api::expr::runtime::CelValue`. It only supports // arena allocation. @@ -66,11 +194,12 @@ class LegacyStructValue final LegacyStructValue( const google::protobuf::Message* absl_nullability_unknown message_ptr, const google::api::expr::runtime:: - LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info) - : message_ptr_(message_ptr), legacy_type_info_(legacy_type_info) {} + LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info); LegacyStructValue(const LegacyStructValue&) = default; + LegacyStructValue(LegacyStructValue&&) = default; LegacyStructValue& operator=(const LegacyStructValue&) = default; + LegacyStructValue& operator=(LegacyStructValue&&) = default; constexpr ValueKind kind() const { return kKind; } @@ -141,6 +270,15 @@ class LegacyStructValue final int* absl_nonnull count) const; using StructValueMixin::Qualify; + const google::protobuf::Message* absl_nullable GetLegacyMessage() const; + + absl::Status WrapLegacyMessageField( + const google::protobuf::FieldDescriptor* absl_nonnull field_descriptor, + ProtoWrapperTypeOptions unboxing_option, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Value* absl_nonnull out) const; + const google::protobuf::Message* absl_nullability_unknown message_ptr() const { return message_ptr_; } @@ -152,6 +290,7 @@ class LegacyStructValue final friend void swap(LegacyStructValue& lhs, LegacyStructValue& rhs) noexcept { using std::swap; + swap(lhs.dispatcher_, rhs.dispatcher_); swap(lhs.message_ptr_, rhs.message_ptr_); swap(lhs.legacy_type_info_, rhs.legacy_type_info_); } @@ -160,6 +299,7 @@ class LegacyStructValue final friend class common_internal::ValueMixin; friend class common_internal::StructValueMixin; + const LegacyStructValueDispatcher* absl_nullable dispatcher_ = nullptr; const google::protobuf::Message* absl_nullability_unknown message_ptr_ = nullptr; const google::api::expr::runtime::LegacyTypeInfoApis* absl_nullability_unknown legacy_type_info_ = nullptr; @@ -176,6 +316,8 @@ LegacyStructValue GetLegacyStructValue(const Value& value); absl::optional AsLegacyStructValue(const Value& value); +const google::protobuf::Message* absl_nullable GetLegacyMessage(const Value& value); + } // namespace common_internal } // namespace cel diff --git a/common/values/list_value_builder.h b/common/values/list_value_builder.h index d7af362c9..8e3b1c2b2 100644 --- a/common/values/list_value_builder.h +++ b/common/values/list_value_builder.h @@ -20,13 +20,9 @@ #include "absl/base/attributes.h" #include "absl/base/nullability.h" #include "absl/status/status.h" -#include "absl/status/statusor.h" #include "common/native_type.h" #include "common/value.h" -#include "eval/public/cel_value.h" #include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" namespace cel { @@ -34,28 +30,6 @@ class ValueFactory; namespace common_internal { -// Special implementation of list which is both a modern list and legacy list. -// Do not try this at home. This should only be implemented in -// `list_value_builder.cc`. -class CompatListValue : public CustomListValueInterface, - public google::api::expr::runtime::CelList { - public: - virtual google::protobuf::Arena* GetOwningArena() const = 0; - - private: - NativeTypeId GetNativeTypeId() const final { - return NativeTypeId::For(); - } -}; - -const CompatListValue* absl_nonnull EmptyCompatListValue(); - -absl::StatusOr MakeCompatListValue( - const CustomListValue& value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena); - // Extension of ParsedListValueInterface which is also mutable. Accessing this // like a normal list before all elements are finished being appended is a bug. // This is primarily used by the runtime to efficiently implement comprehensions @@ -74,22 +48,6 @@ class MutableListValue : public CustomListValueInterface { } }; -// Special implementation of list which is both a modern list, legacy list, and -// mutable. -// -// NOTE: We do not extend CompatListValue to avoid having to use virtual -// inheritance and `dynamic_cast`. -class MutableCompatListValue : public MutableListValue, - public google::api::expr::runtime::CelList { - public: - virtual google::protobuf::Arena* GetOwningArena() const = 0; - - private: - NativeTypeId GetNativeTypeId() const final { - return NativeTypeId::For(); - } -}; - MutableListValue* absl_nonnull NewMutableListValue( google::protobuf::Arena* absl_nonnull arena ABSL_ATTRIBUTE_LIFETIME_BOUND); diff --git a/common/values/map_value_builder.h b/common/values/map_value_builder.h index 2eb70b613..4e67b56f0 100644 --- a/common/values/map_value_builder.h +++ b/common/values/map_value_builder.h @@ -20,13 +20,9 @@ #include "absl/base/attributes.h" #include "absl/base/nullability.h" #include "absl/status/status.h" -#include "absl/status/statusor.h" #include "common/native_type.h" #include "common/value.h" -#include "eval/public/cel_value.h" #include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" namespace cel { @@ -34,28 +30,6 @@ class ValueFactory; namespace common_internal { -// Special implementation of map which is both a modern map and legacy map. Do -// not try this at home. This should only be implemented in -// `map_value_builder.cc`. -class CompatMapValue : public CustomMapValueInterface, - public google::api::expr::runtime::CelMap { - public: - virtual google::protobuf::Arena* GetOwningArena() const = 0; - - private: - NativeTypeId GetNativeTypeId() const final { - return NativeTypeId::For(); - } -}; - -const CompatMapValue* absl_nonnull EmptyCompatMapValue(); - -absl::StatusOr MakeCompatMapValue( - const CustomMapValue& value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena); - // Extension of ParsedMapValueInterface which is also mutable. Accessing this // like a normal map before all entries are finished being inserted is a bug. // This is primarily used by the runtime to efficiently implement comprehensions @@ -74,22 +48,6 @@ class MutableMapValue : public CustomMapValueInterface { } }; -// Special implementation of map which is both a modern map, legacy map, and -// mutable. -// -// NOTE: We do not extend CompatMapValue to avoid having to use virtual -// inheritance and `dynamic_cast`. -class MutableCompatMapValue : public MutableMapValue, - public google::api::expr::runtime::CelMap { - public: - virtual google::protobuf::Arena* GetOwningArena() const = 0; - - private: - NativeTypeId GetNativeTypeId() const final { - return NativeTypeId::For(); - } -}; - MutableMapValue* absl_nonnull NewMutableMapValue( google::protobuf::Arena* absl_nonnull arena); diff --git a/common/values/parsed_message_value.h b/common/values/parsed_message_value.h index 1d02a4369..f736ab69a 100644 --- a/common/values/parsed_message_value.h +++ b/common/values/parsed_message_value.h @@ -106,6 +106,9 @@ class ParsedMessageValue final const google::protobuf::Message* absl_nonnull operator->() const { return value_; } const google::protobuf::Message* absl_nonnull message() const { return value_; } + // Internal: returns true if this value was created without an arena. + bool is_unsafe() const { return arena_ == nullptr; } + bool IsZeroValue() const; std::string DebugString() const; @@ -216,8 +219,6 @@ class ParsedMessageValue final return absl::OkStatus(); } - bool is_unsafe() const { return arena_ == nullptr; } - const google::protobuf::Message* absl_nonnull value_; // The arena attributed as Owning this value. Null if the value is created by diff --git a/common/values/value_builder.cc b/common/values/value_builder.cc index 526bd4adc..f490fb9c7 100644 --- a/common/values/value_builder.cc +++ b/common/values/value_builder.cc @@ -36,14 +36,12 @@ #include "absl/types/optional.h" #include "absl/types/span.h" #include "common/allocator.h" -#include "common/legacy_value.h" #include "common/native_type.h" #include "common/type.h" #include "common/value.h" #include "common/value_kind.h" #include "common/values/list_value_builder.h" #include "common/values/map_value_builder.h" -#include "eval/public/cel_value.h" #include "internal/casts.h" #include "internal/manual.h" #include "internal/status_macros.h" @@ -61,7 +59,6 @@ namespace { using ::cel::well_known_types::ListValueReflection; using ::cel::well_known_types::StructReflection; using ::cel::well_known_types::ValueReflection; -using ::google::api::expr::runtime::CelValue; using ValueVector = std::vector>; @@ -122,9 +119,9 @@ absl::Status ListValueToJson( reflection.MutableListValue(json)); } -class CompatListValueImplIterator final : public ValueIterator { +class ListValueImplIterator final : public ValueIterator { public: - explicit CompatListValueImplIterator(absl::Span elements) + explicit ListValueImplIterator(absl::Span elements) : elements_(elements) {} bool HasNext() override { return index_ < elements_.size(); } @@ -198,6 +195,8 @@ struct ValueFormatter { } }; +class ListValueImpl; + class ListValueBuilderImpl final : public ListValueBuilder { public: explicit ListValueBuilderImpl(google::protobuf::Arena* absl_nonnull arena) @@ -224,25 +223,20 @@ class ListValueBuilderImpl final : public ListValueBuilder { CustomListValue BuildCustom() &&; - const CompatListValue* absl_nonnull BuildCompat() &&; + const ListValueImpl* absl_nonnull BuildImpl() &&; - const CompatListValue* absl_nonnull BuildCompatAt( - void* absl_nonnull address) &&; + const ListValueImpl* absl_nonnull BuildAt(void* absl_nonnull address) &&; private: google::protobuf::Arena* absl_nonnull const arena_; internal::Manual elements_; }; -class CompatListValueImpl final : public CompatListValue { +class ListValueImpl final : public CustomListValueInterface { public: - explicit CompatListValueImpl(ValueVector&& elements) + explicit ListValueImpl(ValueVector&& elements) : elements_(std::move(elements)) {} - google::protobuf::Arena* GetOwningArena() const override { - return elements_.get_allocator().arena(); - } - std::string DebugString() const override { return absl::StrCat("[", absl::StrJoin(elements_, ", ", ValueFormatter{}), "]"); @@ -285,32 +279,10 @@ class CompatListValueImpl final : public CompatListValue { } absl::StatusOr NewIterator() const override { - return std::make_unique( + return std::make_unique( absl::MakeConstSpan(elements_)); } - CelValue operator[](int index) const override { - return Get(elements_.get_allocator().arena(), index); - } - - // Like `operator[](int)` above, but also accepts an arena. Prefer calling - // this variant if the arena is known. - CelValue Get(google::protobuf::Arena* arena, int index) const override { - if (arena == nullptr) { - arena = elements_.get_allocator().arena(); - } - if (ABSL_PREDICT_FALSE(index < 0 || index >= size())) { - return CelValue::CreateError(google::protobuf::Arena::Create( - arena, common_internal::MakeIndexOutOfBoundsError(index))); - } - return common_internal::UnsafeLegacyValue( - elements_[index], - /*stable=*/true, - arena != nullptr ? arena : elements_.get_allocator().arena()); - } - - int size() const override { return static_cast(Size()); } - protected: absl::Status Get(size_t index, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, @@ -326,6 +298,10 @@ class CompatListValueImpl final : public CompatListValue { } private: + NativeTypeId GetNativeTypeId() const override { + return NativeTypeId::For(); + } + const ValueVector elements_; }; @@ -338,35 +314,28 @@ ListValue ListValueBuilderImpl::Build() && { CustomListValue ListValueBuilderImpl::BuildCustom() && { if (elements_->empty()) { - return CustomListValue(EmptyCompatListValue(), arena_); + return CustomListValue(); } - return CustomListValue(std::move(*this).BuildCompat(), arena_); + return CustomListValue(std::move(*this).BuildImpl(), arena_); } -const CompatListValue* absl_nonnull ListValueBuilderImpl::BuildCompat() && { - if (elements_->empty()) { - return EmptyCompatListValue(); - } - return std::move(*this).BuildCompatAt(arena_->AllocateAligned( - sizeof(CompatListValueImpl), alignof(CompatListValueImpl))); +const ListValueImpl* absl_nonnull ListValueBuilderImpl::BuildImpl() && { + return std::move(*this).BuildAt( + arena_->AllocateAligned(sizeof(ListValueImpl), alignof(ListValueImpl))); } -const CompatListValue* absl_nonnull ListValueBuilderImpl::BuildCompatAt( +const ListValueImpl* absl_nonnull ListValueBuilderImpl::BuildAt( void* absl_nonnull address) && { - CompatListValueImpl* absl_nonnull impl = - ::new (address) CompatListValueImpl(std::move(*elements_)); + ListValueImpl* absl_nonnull impl = + ::new (address) ListValueImpl(std::move(*elements_)); return impl; } -class MutableCompatListValueImpl final : public MutableCompatListValue { +class MutableListValueImpl final : public MutableListValue { public: - explicit MutableCompatListValueImpl(google::protobuf::Arena* absl_nonnull arena) + explicit MutableListValueImpl(google::protobuf::Arena* absl_nonnull arena) : elements_(arena) {} - google::protobuf::Arena* GetOwningArena() const override { - return elements_.get_allocator().arena(); - } - std::string DebugString() const override { return absl::StrCat("[", absl::StrJoin(elements_, ", ", ValueFormatter{}), "]"); @@ -409,31 +378,10 @@ class MutableCompatListValueImpl final : public MutableCompatListValue { } absl::StatusOr NewIterator() const override { - return std::make_unique( + return std::make_unique( absl::MakeConstSpan(elements_)); } - CelValue operator[](int index) const override { - return Get(elements_.get_allocator().arena(), index); - } - - // Like `operator[](int)` above, but also accepts an arena. Prefer calling - // this variant if the arena is known. - CelValue Get(google::protobuf::Arena* arena, int index) const override { - if (arena == nullptr) { - arena = elements_.get_allocator().arena(); - } - if (ABSL_PREDICT_FALSE(index < 0 || index >= size())) { - return CelValue::CreateError(google::protobuf::Arena::Create( - arena, common_internal::MakeIndexOutOfBoundsError(index))); - } - return common_internal::UnsafeLegacyValue( - elements_[index], /*stable=*/false, - arena != nullptr ? arena : elements_.get_allocator().arena()); - } - - int size() const override { return static_cast(Size()); } - absl::Status Append(Value value) const override { CEL_RETURN_IF_ERROR(CheckListElement(value)); elements_.emplace_back(std::move(value)); @@ -462,64 +410,36 @@ class MutableCompatListValueImpl final : public MutableCompatListValue { } // namespace -absl::StatusOr MakeCompatListValue( - const CustomListValue& value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - ListValueBuilderImpl builder(arena); - builder.Reserve(value.Size()); - - CEL_RETURN_IF_ERROR(value.ForEach( - [&](const Value& element) -> absl::StatusOr { - CEL_RETURN_IF_ERROR(builder.Add(element)); - return true; - }, - descriptor_pool, message_factory, arena)); - - return std::move(builder).BuildCompat(); -} - MutableListValue* absl_nonnull NewMutableListValue( google::protobuf::Arena* absl_nonnull arena) { - return ::new (arena->AllocateAligned(sizeof(MutableCompatListValueImpl), - alignof(MutableCompatListValueImpl))) - MutableCompatListValueImpl(arena); + return ::new (arena->AllocateAligned(sizeof(MutableListValueImpl), + alignof(MutableListValueImpl))) + MutableListValueImpl(arena); } bool IsMutableListValue(const Value& value) { if (auto custom_list_value = value.AsCustomList(); custom_list_value) { - NativeTypeId native_type_id = custom_list_value->GetTypeId(); - if (native_type_id == NativeTypeId::For() || - native_type_id == NativeTypeId::For()) { - return true; - } + return custom_list_value->GetTypeId() == + NativeTypeId::For(); } return false; } bool IsMutableListValue(const ListValue& value) { if (auto custom_list_value = value.AsCustom(); custom_list_value) { - NativeTypeId native_type_id = custom_list_value->GetTypeId(); - if (native_type_id == NativeTypeId::For() || - native_type_id == NativeTypeId::For()) { - return true; - } + return custom_list_value->GetTypeId() == + NativeTypeId::For(); } return false; } const MutableListValue* absl_nullable AsMutableListValue(const Value& value) { if (auto custom_list_value = value.AsCustomList(); custom_list_value) { - NativeTypeId native_type_id = custom_list_value->GetTypeId(); - if (native_type_id == NativeTypeId::For()) { + if (custom_list_value->GetTypeId() == + NativeTypeId::For()) { return cel::internal::down_cast( custom_list_value->interface()); } - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - custom_list_value->interface()); - } } return nullptr; } @@ -527,47 +447,25 @@ const MutableListValue* absl_nullable AsMutableListValue(const Value& value) { const MutableListValue* absl_nullable AsMutableListValue( const ListValue& value) { if (auto custom_list_value = value.AsCustom(); custom_list_value) { - NativeTypeId native_type_id = custom_list_value->GetTypeId(); - if (native_type_id == NativeTypeId::For()) { + if (custom_list_value->GetTypeId() == + NativeTypeId::For()) { return cel::internal::down_cast( custom_list_value->interface()); } - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - custom_list_value->interface()); - } } return nullptr; } const MutableListValue& GetMutableListValue(const Value& value) { ABSL_DCHECK(IsMutableListValue(value)) << value; - const auto& custom_list_value = value.GetCustomList(); - NativeTypeId native_type_id = custom_list_value.GetTypeId(); - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - *custom_list_value.interface()); - } - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - *custom_list_value.interface()); - } - ABSL_UNREACHABLE(); + return cel::internal::down_cast( + *value.GetCustomList().interface()); } const MutableListValue& GetMutableListValue(const ListValue& value) { ABSL_DCHECK(IsMutableListValue(value)) << value; - const auto& custom_list_value = value.GetCustom(); - NativeTypeId native_type_id = custom_list_value.GetTypeId(); - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - *custom_list_value.interface()); - } - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - *custom_list_value.interface()); - } - ABSL_UNREACHABLE(); + return cel::internal::down_cast( + *value.GetCustom().interface()); } absl_nonnull cel::ListValueBuilderPtr NewListValueBuilder( @@ -585,9 +483,6 @@ namespace common_internal { namespace { -using ::google::api::expr::runtime::CelList; -using ::google::api::expr::runtime::CelValue; - absl::Status CheckMapValue(const Value& value) { if (auto error_value = value.AsError(); ABSL_PREDICT_FALSE(error_value)) { return error_value->ToStatus(); @@ -616,21 +511,6 @@ size_t ValueHash(const Value& value) { } } -size_t ValueHash(const CelValue& value) { - switch (value.type()) { - case CelValue::Type::kBool: - return absl::HashOf(ValueKind::kBool, value.BoolOrDie()); - case CelValue::Type::kInt: - return absl::HashOf(ValueKind::kInt, value.Int64OrDie()); - case CelValue::Type::kUint: - return absl::HashOf(ValueKind::kUint, value.Uint64OrDie()); - case CelValue::Type::kString: - return absl::HashOf(ValueKind::kString, value.StringOrDie().value()); - default: - ABSL_UNREACHABLE(); - } -} - bool ValueEquals(const Value& lhs, const Value& rhs) { switch (lhs.kind()) { case ValueKind::kBool: @@ -690,65 +570,6 @@ bool ValueEquals(const Value& lhs, const Value& rhs) { } } -bool CelValueEquals(const CelValue& lhs, const Value& rhs) { - switch (lhs.type()) { - case CelValue::Type::kBool: - switch (rhs.kind()) { - case ValueKind::kBool: - return BoolValue(lhs.BoolOrDie()) == rhs.GetBool(); - case ValueKind::kInt: - return false; - case ValueKind::kUint: - return false; - case ValueKind::kString: - return false; - default: - ABSL_UNREACHABLE(); - } - case CelValue::Type::kInt: - switch (rhs.kind()) { - case ValueKind::kBool: - return false; - case ValueKind::kInt: - return IntValue(lhs.Int64OrDie()) == rhs.GetInt(); - case ValueKind::kUint: - return false; - case ValueKind::kString: - return false; - default: - ABSL_UNREACHABLE(); - } - case CelValue::Type::kUint: - switch (rhs.kind()) { - case ValueKind::kBool: - return false; - case ValueKind::kInt: - return false; - case ValueKind::kUint: - return UintValue(lhs.Uint64OrDie()) == rhs.GetUint(); - case ValueKind::kString: - return false; - default: - ABSL_UNREACHABLE(); - } - case CelValue::Type::kString: - switch (rhs.kind()) { - case ValueKind::kBool: - return false; - case ValueKind::kInt: - return false; - case ValueKind::kUint: - return false; - case ValueKind::kString: - return rhs.GetString().Equals(lhs.StringOrDie().value()); - default: - ABSL_UNREACHABLE(); - } - default: - ABSL_UNREACHABLE(); - } -} - absl::StatusOr ValueToJsonString(const Value& value) { switch (value.kind()) { case ValueKind::kString: @@ -805,24 +626,10 @@ absl::Status MapValueToJson( } struct ValueHasher { - using is_transparent = void; - size_t operator()(const Value& value) const { return (ValueHash)(value); } - - size_t operator()(const CelValue& value) const { return (ValueHash)(value); } }; struct ValueEqualer { - using is_transparent = void; - - bool operator()(const Value& lhs, const CelValue& rhs) const { - return (*this)(rhs, lhs); - } - - bool operator()(const CelValue& lhs, const Value& rhs) const { - return (CelValueEquals)(lhs, rhs); - } - bool operator()(const Value& lhs, const Value& rhs) const { return (ValueEquals)(lhs, rhs); } @@ -834,9 +641,9 @@ using ValueFlatHashMap = absl::flat_hash_map; -class CompatMapValueImplIterator final : public ValueIterator { +class MapValueImplIterator final : public ValueIterator { public: - explicit CompatMapValueImplIterator(const ValueFlatHashMap* absl_nonnull map) + explicit MapValueImplIterator(const ValueFlatHashMap* absl_nonnull map) : begin_(map->begin()), end_(map->end()) {} bool HasNext() override { return begin_ != end_; } @@ -899,6 +706,8 @@ class CompatMapValueImplIterator final : public ValueIterator { const typename ValueFlatHashMap::const_iterator end_; }; +class MapValueImpl; + class MapValueBuilderImpl final : public MapValueBuilder { public: explicit MapValueBuilderImpl(google::protobuf::Arena* absl_nonnull arena) @@ -929,20 +738,16 @@ class MapValueBuilderImpl final : public MapValueBuilder { CustomMapValue BuildCustom() &&; - const CompatMapValue* absl_nonnull BuildCompat() &&; + const MapValueImpl* absl_nonnull BuildImpl() &&; private: google::protobuf::Arena* absl_nonnull const arena_; internal::Manual map_; }; -class CompatMapValueImpl final : public CompatMapValue { +class MapValueImpl final : public CustomMapValueInterface { public: - explicit CompatMapValueImpl(ValueFlatHashMap&& map) : map_(std::move(map)) {} - - google::protobuf::Arena* GetOwningArena() const override { - return map_.get_allocator().arena(); - } + explicit MapValueImpl(ValueFlatHashMap&& map) : map_(std::move(map)) {} std::string DebugString() const override { return absl::StrCat("{", absl::StrJoin(map_, ", ", ValueFormatter{}), "}"); @@ -992,42 +797,7 @@ class CompatMapValueImpl final : public CompatMapValue { } absl::StatusOr NewIterator() const override { - return std::make_unique(&map_); - } - - absl::optional operator[](CelValue key) const override { - return Get(map_.get_allocator().arena(), key); - } - - using CompatMapValue::Get; - absl::optional Get(google::protobuf::Arena* arena, - CelValue key) const override { - if (auto status = CelValue::CheckMapKeyType(key); !status.ok()) { - status.IgnoreError(); - return std::nullopt; - } - if (auto it = map_.find(key); it != map_.end()) { - return common_internal::UnsafeLegacyValue( - it->second, /*stable=*/true, - arena != nullptr ? arena : map_.get_allocator().arena()); - } - return std::nullopt; - } - - absl::StatusOr Has(const CelValue& key) const override { - // This check safeguards against issues with invalid key types such as NaN. - CEL_RETURN_IF_ERROR(CelValue::CheckMapKeyType(key)); - return map_.find(key) != map_.end(); - } - - int size() const override { return static_cast(Size()); } - - absl::StatusOr ListKeys() const override { - return ProjectKeys(); - } - - absl::StatusOr ListKeys(google::protobuf::Arena* arena) const override { - return ProjectKeys(); + return std::make_unique(&map_); } protected: @@ -1055,7 +825,11 @@ class CompatMapValueImpl final : public CompatMapValue { } private: - const CompatListValue* absl_nonnull ProjectKeys() const { + NativeTypeId GetNativeTypeId() const override { + return NativeTypeId::For(); + } + + const CustomListValueInterface* absl_nonnull ProjectKeys() const { absl::call_once(keys_once_, [this]() { ListValueBuilderImpl builder(map_.get_allocator().arena()); builder.Reserve(map_.size()); @@ -1064,15 +838,14 @@ class CompatMapValueImpl final : public CompatMapValue { builder.UnsafeAdd(entry.first); } - std::move(builder).BuildCompatAt(&keys_[0]); + std::move(builder).BuildAt(&keys_[0]); }); - return std::launder( - reinterpret_cast(&keys_[0])); + return std::launder(reinterpret_cast(&keys_[0])); } const ValueFlatHashMap map_; mutable absl::once_flag keys_once_; - alignas(CompatListValueImpl) mutable char keys_[sizeof(CompatListValueImpl)]; + alignas(ListValueImpl) mutable char keys_[sizeof(ListValueImpl)]; }; MapValue MapValueBuilderImpl::Build() && { @@ -1084,30 +857,23 @@ MapValue MapValueBuilderImpl::Build() && { CustomMapValue MapValueBuilderImpl::BuildCustom() && { if (map_->empty()) { - return CustomMapValue(EmptyCompatMapValue(), arena_); + return CustomMapValue(); } - return CustomMapValue(std::move(*this).BuildCompat(), arena_); + return CustomMapValue(std::move(*this).BuildImpl(), arena_); } -const CompatMapValue* absl_nonnull MapValueBuilderImpl::BuildCompat() && { - if (map_->empty()) { - return EmptyCompatMapValue(); - } - CompatMapValueImpl* absl_nonnull impl = ::new (arena_->AllocateAligned( - sizeof(CompatMapValueImpl), alignof(CompatMapValueImpl))) - CompatMapValueImpl(std::move(*map_)); +const MapValueImpl* absl_nonnull MapValueBuilderImpl::BuildImpl() && { + MapValueImpl* absl_nonnull impl = ::new ( + arena_->AllocateAligned(sizeof(MapValueImpl), alignof(MapValueImpl))) + MapValueImpl(std::move(*map_)); return impl; } -class MutableCompatMapValueImpl final : public MutableCompatMapValue { +class MutableMapValueImpl final : public MutableMapValue { public: - explicit MutableCompatMapValueImpl(google::protobuf::Arena* absl_nonnull arena) + explicit MutableMapValueImpl(google::protobuf::Arena* absl_nonnull arena) : map_(arena) {} - google::protobuf::Arena* GetOwningArena() const override { - return map_.get_allocator().arena(); - } - std::string DebugString() const override { return absl::StrCat("{", absl::StrJoin(map_, ", ", ValueFormatter{}), "}"); } @@ -1156,42 +922,7 @@ class MutableCompatMapValueImpl final : public MutableCompatMapValue { } absl::StatusOr NewIterator() const override { - return std::make_unique(&map_); - } - - absl::optional operator[](CelValue key) const override { - return Get(map_.get_allocator().arena(), key); - } - - using MutableCompatMapValue::Get; - absl::optional Get(google::protobuf::Arena* arena, - CelValue key) const override { - if (auto status = CelValue::CheckMapKeyType(key); !status.ok()) { - status.IgnoreError(); - return std::nullopt; - } - if (auto it = map_.find(key); it != map_.end()) { - return common_internal::UnsafeLegacyValue( - it->second, /*stable=*/false, - arena != nullptr ? arena : map_.get_allocator().arena()); - } - return std::nullopt; - } - - absl::StatusOr Has(const CelValue& key) const override { - // This check safeguards against issues with invalid key types such as NaN. - CEL_RETURN_IF_ERROR(CelValue::CheckMapKeyType(key)); - return map_.find(key) != map_.end(); - } - - int size() const override { return static_cast(Size()); } - - absl::StatusOr ListKeys() const override { - return ProjectKeys(); - } - - absl::StatusOr ListKeys(google::protobuf::Arena* arena) const override { - return ProjectKeys(); + return std::make_unique(&map_); } absl::Status Put(Value key, Value value) const override { @@ -1232,7 +963,7 @@ class MutableCompatMapValueImpl final : public MutableCompatMapValue { } private: - const CompatListValue* absl_nonnull ProjectKeys() const { + const CustomListValueInterface* absl_nonnull ProjectKeys() const { absl::call_once(keys_once_, [this]() { ListValueBuilderImpl builder(map_.get_allocator().arena()); builder.Reserve(map_.size()); @@ -1241,124 +972,71 @@ class MutableCompatMapValueImpl final : public MutableCompatMapValue { builder.UnsafeAdd(entry.first); } - std::move(builder).BuildCompatAt(&keys_[0]); + std::move(builder).BuildAt(&keys_[0]); }); - return std::launder( - reinterpret_cast(&keys_[0])); + return std::launder(reinterpret_cast(&keys_[0])); } mutable ValueFlatHashMap map_; mutable absl::once_flag keys_once_; - alignas(CompatListValueImpl) mutable char keys_[sizeof(CompatListValueImpl)]; + alignas(ListValueImpl) mutable char keys_[sizeof(ListValueImpl)]; }; } // namespace -absl::StatusOr MakeCompatMapValue( - const CustomMapValue& value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - MapValueBuilderImpl builder(arena); - builder.Reserve(value.Size()); - - CEL_RETURN_IF_ERROR(value.ForEach( - [&](const Value& key, const Value& value) -> absl::StatusOr { - CEL_RETURN_IF_ERROR(builder.Put(key, value)); - return true; - }, - descriptor_pool, message_factory, arena)); - - return std::move(builder).BuildCompat(); -} - MutableMapValue* absl_nonnull NewMutableMapValue( google::protobuf::Arena* absl_nonnull arena) { - return ::new (arena->AllocateAligned(sizeof(MutableCompatMapValueImpl), - alignof(MutableCompatMapValueImpl))) - MutableCompatMapValueImpl(arena); + return ::new (arena->AllocateAligned(sizeof(MutableMapValueImpl), + alignof(MutableMapValueImpl))) + MutableMapValueImpl(arena); } bool IsMutableMapValue(const Value& value) { if (auto custom_map_value = value.AsCustomMap(); custom_map_value) { - NativeTypeId native_type_id = custom_map_value->GetTypeId(); - if (native_type_id == NativeTypeId::For() || - native_type_id == NativeTypeId::For()) { - return true; - } + return custom_map_value->GetTypeId() == + NativeTypeId::For(); } return false; } bool IsMutableMapValue(const MapValue& value) { if (auto custom_map_value = value.AsCustom(); custom_map_value) { - NativeTypeId native_type_id = custom_map_value->GetTypeId(); - if (native_type_id == NativeTypeId::For() || - native_type_id == NativeTypeId::For()) { - return true; - } + return custom_map_value->GetTypeId() == + NativeTypeId::For(); } return false; } const MutableMapValue* absl_nullable AsMutableMapValue(const Value& value) { if (auto custom_map_value = value.AsCustomMap(); custom_map_value) { - NativeTypeId native_type_id = custom_map_value->GetTypeId(); - if (native_type_id == NativeTypeId::For()) { + if (custom_map_value->GetTypeId() == NativeTypeId::For()) { return cel::internal::down_cast( custom_map_value->interface()); } - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - custom_map_value->interface()); - } } return nullptr; } const MutableMapValue* absl_nullable AsMutableMapValue(const MapValue& value) { if (auto custom_map_value = value.AsCustom(); custom_map_value) { - NativeTypeId native_type_id = custom_map_value->GetTypeId(); - if (native_type_id == NativeTypeId::For()) { + if (custom_map_value->GetTypeId() == NativeTypeId::For()) { return cel::internal::down_cast( custom_map_value->interface()); } - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - custom_map_value->interface()); - } } return nullptr; } const MutableMapValue& GetMutableMapValue(const Value& value) { ABSL_DCHECK(IsMutableMapValue(value)) << value; - const auto& custom_map_value = value.GetCustomMap(); - NativeTypeId native_type_id = custom_map_value.GetTypeId(); - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - *custom_map_value.interface()); - } - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - *custom_map_value.interface()); - } - ABSL_UNREACHABLE(); + return cel::internal::down_cast( + *value.GetCustomMap().interface()); } const MutableMapValue& GetMutableMapValue(const MapValue& value) { ABSL_DCHECK(IsMutableMapValue(value)) << value; - const auto& custom_map_value = value.GetCustom(); - NativeTypeId native_type_id = custom_map_value.GetTypeId(); - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - *custom_map_value.interface()); - } - if (native_type_id == NativeTypeId::For()) { - return cel::internal::down_cast( - *custom_map_value.interface()); - } - ABSL_UNREACHABLE(); + return cel::internal::down_cast( + *value.GetCustom().interface()); } absl_nonnull cel::MapValueBuilderPtr NewMapValueBuilder( diff --git a/env/BUILD b/env/BUILD index 0c17d6305..86a2d7036 100644 --- a/env/BUILD +++ b/env/BUILD @@ -102,13 +102,13 @@ cc_library( "//compiler:optional", "//extensions:bindings_ext", "//extensions:comprehensions_v2", - "//extensions:encoders", + "//extensions:encoders_modern", "//extensions:lists_functions", "//extensions:math_ext_decls", "//extensions:proto_ext", - "//extensions:regex_ext", - "//extensions:sets_functions", - "//extensions:strings", + "//extensions:regex_ext_modern", + "//extensions:sets_functions_modern", + "//extensions:strings_modern", ], ) @@ -149,13 +149,13 @@ cc_library( ":env_runtime", "//checker:optional", "//env/internal:runtime_ext_registry", - "//extensions:encoders", + "//extensions:encoders_modern", "//extensions:lists_functions", - "//extensions:math_ext", "//extensions:math_ext_decls", - "//extensions:regex_ext", - "//extensions:sets_functions", - "//extensions:strings", + "//extensions:math_ext_modern", + "//extensions:regex_ext_modern", + "//extensions:sets_functions_modern", + "//extensions:strings_modern", "//runtime:optional_types", "//runtime:runtime_builder", "//runtime:runtime_options", diff --git a/env/env_std_extensions.cc b/env/env_std_extensions.cc index f2041b979..d77d7a138 100644 --- a/env/env_std_extensions.cc +++ b/env/env_std_extensions.cc @@ -19,13 +19,13 @@ #include "env/env.h" #include "extensions/bindings_ext.h" #include "extensions/comprehensions_v2.h" -#include "extensions/encoders.h" +#include "extensions/encoders_modern.h" #include "extensions/lists_functions.h" #include "extensions/math_ext_decls.h" #include "extensions/proto_ext.h" -#include "extensions/regex_ext.h" -#include "extensions/sets_functions.h" -#include "extensions/strings.h" +#include "extensions/regex_ext_modern.h" +#include "extensions/sets_functions_modern.h" +#include "extensions/strings_modern.h" namespace cel { diff --git a/env/runtime_std_extensions.cc b/env/runtime_std_extensions.cc index b866a5965..d7d8a65fa 100644 --- a/env/runtime_std_extensions.cc +++ b/env/runtime_std_extensions.cc @@ -19,13 +19,13 @@ #include "checker/optional.h" #include "env/env_runtime.h" #include "env/internal/runtime_ext_registry.h" -#include "extensions/encoders.h" +#include "extensions/encoders_modern.h" #include "extensions/lists_functions.h" -#include "extensions/math_ext.h" #include "extensions/math_ext_decls.h" -#include "extensions/regex_ext.h" -#include "extensions/sets_functions.h" -#include "extensions/strings.h" +#include "extensions/math_ext_modern.h" +#include "extensions/regex_ext_modern.h" +#include "extensions/sets_functions_modern.h" +#include "extensions/strings_modern.h" #include "runtime/optional_types.h" #include "runtime/runtime_builder.h" #include "runtime/runtime_options.h" diff --git a/eval/compiler/BUILD b/eval/compiler/BUILD index a5cfa29e0..1d4713684 100644 --- a/eval/compiler/BUILD +++ b/eval/compiler/BUILD @@ -197,7 +197,8 @@ cc_test( "//runtime:function_adapter", "//runtime:runtime_options", "//runtime:standard_functions", - "//runtime/internal:runtime_env_testing", + "//runtime/internal:legacy_runtime_env", + "//runtime/internal:legacy_runtime_env_testing", "@cel_conformance//proto/cel/expr/conformance/proto3:test_all_types_cc_proto", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/log:absl_check", @@ -242,8 +243,9 @@ cc_test( "//parser:parser_interface", "//runtime:activation", "//runtime:runtime_options", + "//runtime/internal:legacy_runtime_env", + "//runtime/internal:legacy_runtime_env_testing", "//runtime/internal:runtime_env", - "//runtime/internal:runtime_env_testing", "//testutil:test_macros", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", @@ -273,7 +275,9 @@ cc_library( "//internal:status_macros", "//runtime:runtime_issue", "//runtime:runtime_options", - "//runtime/internal:runtime_env", + "//runtime:type_registry", + "//runtime/internal:legacy_runtime_env", + "//runtime/internal:legacy_runtime_type_provider", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/log:absl_check", @@ -308,7 +312,7 @@ cc_test( "//parser", "//parser:macro", "//runtime:runtime_options", - "//runtime/internal:runtime_env_testing", + "//runtime/internal:legacy_runtime_env_testing", "@cel_conformance//proto/cel/expr/conformance/proto3:test_all_types_cc_proto", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/status", @@ -508,7 +512,7 @@ cc_test( "//parser", "//parser:options", "//runtime:runtime_options", - "//runtime/internal:runtime_env_testing", + "//runtime/internal:legacy_runtime_env_testing", "@com_google_absl//absl/log:absl_check", "@com_google_absl//absl/status", "@com_google_absl//absl/status:status_matchers", @@ -584,8 +588,8 @@ cc_test( "//runtime:runtime_issue", "//runtime:runtime_options", "//runtime/internal:issue_collector", - "//runtime/internal:runtime_env", - "//runtime/internal:runtime_env_testing", + "//runtime/internal:legacy_runtime_env", + "//runtime/internal:legacy_runtime_env_testing", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/status", "@com_google_cel_spec//proto/cel/expr:checked_cc_proto", diff --git a/eval/compiler/cel_expression_builder_flat_impl.cc b/eval/compiler/cel_expression_builder_flat_impl.cc index f519d9393..bd0a1adce 100644 --- a/eval/compiler/cel_expression_builder_flat_impl.cc +++ b/eval/compiler/cel_expression_builder_flat_impl.cc @@ -23,6 +23,7 @@ #include "cel/expr/checked.pb.h" #include "cel/expr/syntax.pb.h" #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/log/check.h" #include "absl/status/status.h" #include "absl/status/statusor.h" @@ -32,7 +33,11 @@ #include "eval/public/cel_expression.h" #include "extensions/protobuf/ast_converters.h" #include "internal/status_macros.h" +#include "runtime/internal/legacy_runtime_env.h" +#include "runtime/internal/legacy_runtime_type_provider.h" #include "runtime/runtime_issue.h" +#include "runtime/runtime_options.h" +#include "runtime/type_registry.h" namespace google::api::expr::runtime { @@ -42,6 +47,20 @@ using ::cel::expr::CheckedExpr; using ::cel::expr::Expr; // NOLINT: adjusted in OSS using ::cel::expr::SourceInfo; +CelExpressionBuilderFlatImpl::CelExpressionBuilderFlatImpl( + absl_nonnull std::shared_ptr env, + const cel::RuntimeOptions& options) + : env_(std::move(env)), + legacy_type_provider_( + std::make_shared( + env_->runtime_env.descriptor_pool.get(), + &cel::runtime_internal::GetRuntimeTypeProvider( + env_->runtime_env.type_registry))), + flat_expr_builder_(env_->GetRuntimeEnv(), options, + legacy_type_provider_.get()) { + env_->runtime_env.KeepAlive(legacy_type_provider_); +} + absl::StatusOr> CelExpressionBuilderFlatImpl::CreateExpression( const Expr* expr, const SourceInfo* source_info, @@ -95,7 +114,8 @@ CelExpressionBuilderFlatImpl::CreateExpressionImpl( } } - return std::make_unique(env_, std::move(impl)); + return std::make_unique(env_->GetRuntimeEnv(), + std::move(impl)); } } // namespace google::api::expr::runtime diff --git a/eval/compiler/cel_expression_builder_flat_impl.h b/eval/compiler/cel_expression_builder_flat_impl.h index 6f47f4ec3..664c81211 100644 --- a/eval/compiler/cel_expression_builder_flat_impl.h +++ b/eval/compiler/cel_expression_builder_flat_impl.h @@ -34,7 +34,8 @@ #include "eval/public/cel_expression.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_type_registry.h" -#include "runtime/internal/runtime_env.h" +#include "runtime/internal/legacy_runtime_env.h" +#include "runtime/internal/legacy_runtime_type_provider.h" #include "runtime/runtime_options.h" namespace google::api::expr::runtime { @@ -44,15 +45,11 @@ namespace google::api::expr::runtime { class CelExpressionBuilderFlatImpl : public CelExpressionBuilder { public: CelExpressionBuilderFlatImpl( - absl_nonnull std::shared_ptr env, - const cel::RuntimeOptions& options) - : env_(std::move(env)), - flat_expr_builder_(env_, options, /*use_legacy_type_provider=*/true) { - ABSL_DCHECK(env_->IsInitialized()); - } + absl_nonnull std::shared_ptr env, + const cel::RuntimeOptions& options); explicit CelExpressionBuilderFlatImpl( - absl_nonnull std::shared_ptr env) + absl_nonnull std::shared_ptr env) : CelExpressionBuilderFlatImpl(std::move(env), cel::RuntimeOptions()) {} absl::StatusOr> CreateExpression( @@ -99,7 +96,10 @@ class CelExpressionBuilderFlatImpl : public CelExpressionBuilder { std::unique_ptr converted_ast, std::vector* warnings) const; - absl_nonnull std::shared_ptr env_; + absl_nonnull std::shared_ptr env_; + absl_nonnull + std::shared_ptr + legacy_type_provider_; FlatExprBuilder flat_expr_builder_; }; diff --git a/eval/compiler/cel_expression_builder_flat_impl_test.cc b/eval/compiler/cel_expression_builder_flat_impl_test.cc index b13f2fb6d..1a1c6d8ce 100644 --- a/eval/compiler/cel_expression_builder_flat_impl_test.cc +++ b/eval/compiler/cel_expression_builder_flat_impl_test.cc @@ -45,7 +45,7 @@ #include "internal/testing.h" #include "parser/macro.h" #include "parser/parser.h" -#include "runtime/internal/runtime_env_testing.h" +#include "runtime/internal/legacy_runtime_env_testing.h" #include "runtime/runtime_options.h" #include "google/protobuf/arena.h" #include "google/protobuf/message.h" @@ -58,7 +58,7 @@ namespace { using ::absl_testing::StatusIs; using ::cel::expr::conformance::proto3::NestedTestAllTypes; using ::cel::expr::conformance::proto3::TestAllTypes; -using ::cel::runtime_internal::NewTestingRuntimeEnv; +using ::cel::runtime_internal::NewTestingLegacyRuntimeEnv; using ::cel::expr::CheckedExpr; using ::cel::expr::Expr; using ::cel::expr::ParsedExpr; @@ -73,7 +73,7 @@ using ::testing::HasSubstr; TEST(CelExpressionBuilderFlatImplTest, Error) { Expr expr; SourceInfo source_info; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Invalid empty expression"))); @@ -82,7 +82,7 @@ TEST(CelExpressionBuilderFlatImplTest, Error) { TEST(CelExpressionBuilderFlatImplTest, ParsedExpr) { ASSERT_OK_AND_ASSIGN(ParsedExpr parsed_expr, Parse("1 + 2")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); ASSERT_OK_AND_ASSIGN(std::unique_ptr plan, @@ -169,7 +169,7 @@ TEST_P(PlanTest, Basic) { cel::RuntimeOptions options; options.container = "cel.expr.conformance.proto3"; google::protobuf::Arena arena; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK(SetupBuilder(builder)); @@ -453,7 +453,7 @@ TEST(CelExpressionBuilderFlatImplTest, ParsedExprWithWarnings) { cel::RuntimeOptions options; options.fail_on_warnings = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); std::vector warnings; ASSERT_OK_AND_ASSIGN( @@ -478,7 +478,7 @@ TEST(CelExpressionBuilderFlatImplTest, EmptyLegacyTypeViewUnsupported) { ASSERT_OK_AND_ASSIGN(ParsedExpr parsed_expr, Parse("x")); cel::RuntimeOptions options; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(std::unique_ptr plan, builder.CreateExpression(&parsed_expr.expr(), @@ -495,7 +495,7 @@ TEST(CelExpressionBuilderFlatImplTest, LegacyTypeViewSupported) { ASSERT_OK_AND_ASSIGN(ParsedExpr parsed_expr, Parse("x")); cel::RuntimeOptions options; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(std::unique_ptr plan, builder.CreateExpression(&parsed_expr.expr(), @@ -515,7 +515,7 @@ TEST(CelExpressionBuilderFlatImplTest, CheckedExpr) { checked_expr.mutable_expr()->Swap(parsed_expr.mutable_expr()); checked_expr.mutable_source_info()->Swap(parsed_expr.mutable_source_info()); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); ASSERT_OK_AND_ASSIGN(std::unique_ptr plan, @@ -535,7 +535,7 @@ TEST(CelExpressionBuilderFlatImplTest, CheckedExprWithWarnings) { cel::RuntimeOptions options; options.fail_on_warnings = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); std::vector warnings; ASSERT_OK_AND_ASSIGN(std::unique_ptr plan, diff --git a/eval/compiler/flat_expr_builder.cc b/eval/compiler/flat_expr_builder.cc index 3626937e9..52fcef912 100644 --- a/eval/compiler/flat_expr_builder.cc +++ b/eval/compiler/flat_expr_builder.cc @@ -92,7 +92,6 @@ using ::cel::AstTraverse; using ::cel::RuntimeIssue; using ::cel::Value; using ::cel::runtime_internal::ConvertConstant; -using ::cel::runtime_internal::GetLegacyRuntimeTypeProvider; using ::cel::runtime_internal::GetRuntimeTypeProvider; using ::cel::runtime_internal::IssueCollector; @@ -963,6 +962,10 @@ class FlatExprVisitor : public cel::AstVisitor { if (options_.enable_typed_field_access) { std::optional operand_type = extension_context_.GetResolvedType(select_expr.operand().id()); + if (enable_optional_types_ && operand_type.has_value() && + operand_type->IsOptional()) { + operand_type = operand_type->GetOptional().GetParameter(); + } if (operand_type.has_value() && operand_type->IsStruct()) { struct_type = operand_type->GetStruct(); if (struct_type.has_value()) { @@ -2468,9 +2471,8 @@ absl::StatusOr FlatExprBuilder::CreateExpressionImpl( std::move(identifiers), value_stack_size); } const cel::TypeProvider& FlatExprBuilder::GetTypeProvider() const { - return use_legacy_type_provider_ - ? static_cast( - GetLegacyRuntimeTypeProvider(type_registry_)) + return custom_type_reflector_ != nullptr + ? *custom_type_reflector_ : GetRuntimeTypeProvider(type_registry_); } diff --git a/eval/compiler/flat_expr_builder.h b/eval/compiler/flat_expr_builder.h index fc6066883..8a4e136ca 100644 --- a/eval/compiler/flat_expr_builder.h +++ b/eval/compiler/flat_expr_builder.h @@ -43,13 +43,14 @@ class FlatExprBuilder { public: FlatExprBuilder( absl_nonnull std::shared_ptr env, - const cel::RuntimeOptions& options, bool use_legacy_type_provider = false) + const cel::RuntimeOptions& options, + const cel::TypeReflector* absl_nullable custom_type_reflector = nullptr) : env_(std::move(env)), options_(options), container_(options.container), function_registry_(env_->function_registry), type_registry_(env_->type_registry), - use_legacy_type_provider_(use_legacy_type_provider) {} + custom_type_reflector_(custom_type_reflector) {} void AddAstTransform(std::unique_ptr transform) { ast_transforms_.push_back(std::move(transform)); @@ -99,7 +100,7 @@ class FlatExprBuilder { // allow built expressions to keep the registries alive. const cel::FunctionRegistry& function_registry_; const cel::TypeRegistry& type_registry_; - bool use_legacy_type_provider_; + const cel::TypeReflector* absl_nullable custom_type_reflector_; std::vector> ast_transforms_; std::vector program_optimizers_; }; diff --git a/eval/compiler/flat_expr_builder_comprehensions_test.cc b/eval/compiler/flat_expr_builder_comprehensions_test.cc index df60a456d..d3034cddb 100644 --- a/eval/compiler/flat_expr_builder_comprehensions_test.cc +++ b/eval/compiler/flat_expr_builder_comprehensions_test.cc @@ -44,8 +44,9 @@ #include "parser/parser.h" #include "parser/parser_interface.h" #include "runtime/activation.h" +#include "runtime/internal/legacy_runtime_env.h" +#include "runtime/internal/legacy_runtime_env_testing.h" #include "runtime/internal/runtime_env.h" -#include "runtime/internal/runtime_env_testing.h" #include "runtime/runtime_options.h" #include "testutil/test_macros.h" #include "google/protobuf/arena.h" @@ -57,7 +58,8 @@ namespace { using ::absl_testing::IsOk; using ::absl_testing::StatusIs; -using ::cel::runtime_internal::NewTestingRuntimeEnv; +using ::cel::runtime_internal::LegacyRuntimeEnv; +using ::cel::runtime_internal::NewTestingLegacyRuntimeEnv; using ::cel::expr::CheckedExpr; using ::cel::expr::ParsedExpr; using ::testing::HasSubstr; @@ -75,7 +77,7 @@ class CelExpressionBuilderFlatImplComprehensionsTest : public testing::Test { TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, NestedComp) { cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto parsed_expr, parser::Parse("[1, 2].filter(x, [3, 4].all(y, x < y))")); @@ -93,7 +95,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, NestedComp) { TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, MapComp) { cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto parsed_expr, parser::Parse("[1, 2].map(x, x * 2)")); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); @@ -114,7 +116,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, MapComp) { TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, ExistsOneTrue) { cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto parsed_expr, parser::Parse("[7].exists_one(a, a == 7)")); @@ -131,7 +133,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, ExistsOneTrue) { TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, ExistsOneFalse) { cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto parsed_expr, parser::Parse("[7, 7].exists_one(a, a == 7)")); @@ -149,7 +151,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, ExistsOneFalse) { TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, ListCompWithUnknowns) { cel::RuntimeOptions options = GetRuntimeOptions(); options.unknown_processing = UnknownProcessingOptions::kAttributeAndFunction; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto parsed_expr, parser::Parse("items.exists(i, i < 0)")); @@ -214,7 +216,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, })pb", &expr); cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); EXPECT_THAT(builder.CreateExpression(&expr).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -267,7 +269,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, &expr); cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.flat_expr_builder().AddProgramOptimizer( CreateComprehensionVulnerabilityCheck()); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); @@ -311,7 +313,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, &expr); cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.flat_expr_builder().AddProgramOptimizer( CreateComprehensionVulnerabilityCheck()); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); @@ -368,7 +370,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, &expr); cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.flat_expr_builder().AddProgramOptimizer( CreateComprehensionVulnerabilityCheck()); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); @@ -436,7 +438,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, &expr); cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.flat_expr_builder().AddProgramOptimizer( CreateComprehensionVulnerabilityCheck()); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); @@ -483,7 +485,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, &expr); cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.flat_expr_builder().AddProgramOptimizer( CreateComprehensionVulnerabilityCheck()); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); @@ -535,7 +537,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, &expr); cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddProgramOptimizer( CreateComprehensionVulnerabilityCheck()); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); @@ -582,7 +584,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, &expr); cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddProgramOptimizer( CreateComprehensionVulnerabilityCheck()); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); @@ -625,7 +627,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, &expr)); cel::RuntimeOptions options = GetRuntimeOptions(); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK(RegisterBuiltinFunctions(builder.GetRegistry())); EXPECT_THAT( @@ -652,8 +654,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, parser->Parse(*source)); cel::RuntimeOptions options = GetRuntimeOptions(); - std::shared_ptr env = - NewTestingRuntimeEnv(); + std::shared_ptr env = NewTestingLegacyRuntimeEnv(); CelExpressionBuilderFlatImpl builder(env, options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); @@ -666,7 +667,8 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, cel::Activation activation; google::protobuf::Arena arena; FlatExpressionEvaluatorState state = flat_expr.MakeEvaluatorState( - env->descriptor_pool.get(), env->MutableMessageFactory(), &arena); + env->runtime_env.descriptor_pool.get(), + env->runtime_env.MutableMessageFactory(), &arena); ASSERT_OK_AND_ASSIGN( cel::Value result, flat_expr.EvaluateWithCallback(activation, /*embedder_context=*/nullptr, @@ -691,8 +693,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, parser->Parse(*source)); cel::RuntimeOptions options = GetRuntimeOptions(); - std::shared_ptr env = - NewTestingRuntimeEnv(); + std::shared_ptr env = NewTestingLegacyRuntimeEnv(); CelExpressionBuilderFlatImpl builder(env, options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); @@ -705,7 +706,8 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, cel::Activation activation; google::protobuf::Arena arena; FlatExpressionEvaluatorState state = flat_expr.MakeEvaluatorState( - env->descriptor_pool.get(), env->MutableMessageFactory(), &arena); + env->runtime_env.descriptor_pool.get(), + env->runtime_env.MutableMessageFactory(), &arena); ASSERT_OK_AND_ASSIGN( cel::Value result, flat_expr.EvaluateWithCallback(activation, /*embedder_context=*/nullptr, @@ -726,8 +728,7 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, parser->Parse(*source)); cel::RuntimeOptions options = GetRuntimeOptions(); - std::shared_ptr env = - NewTestingRuntimeEnv(); + std::shared_ptr env = NewTestingLegacyRuntimeEnv(); CelExpressionBuilderFlatImpl builder(env, options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); @@ -740,7 +741,8 @@ TEST_F(CelExpressionBuilderFlatImplComprehensionsTest, cel::Activation activation; google::protobuf::Arena arena; FlatExpressionEvaluatorState state = flat_expr.MakeEvaluatorState( - env->descriptor_pool.get(), env->MutableMessageFactory(), &arena); + env->runtime_env.descriptor_pool.get(), + env->runtime_env.MutableMessageFactory(), &arena); ASSERT_OK_AND_ASSIGN( cel::Value result, flat_expr.EvaluateWithCallback(activation, /*embedder_context=*/nullptr, diff --git a/eval/compiler/flat_expr_builder_short_circuiting_conformance_test.cc b/eval/compiler/flat_expr_builder_short_circuiting_conformance_test.cc index 9081b4813..e19e1f1df 100644 --- a/eval/compiler/flat_expr_builder_short_circuiting_conformance_test.cc +++ b/eval/compiler/flat_expr_builder_short_circuiting_conformance_test.cc @@ -20,7 +20,7 @@ #include "internal/testing.h" #include "parser/options.h" #include "parser/parser.h" -#include "runtime/internal/runtime_env_testing.h" +#include "runtime/internal/legacy_runtime_env_testing.h" #include "runtime/runtime_options.h" #include "google/protobuf/arena.h" @@ -29,7 +29,7 @@ namespace google::api::expr::runtime { namespace { using ::absl_testing::IsOk; -using ::cel::runtime_internal::NewTestingRuntimeEnv; +using ::cel::runtime_internal::NewTestingLegacyRuntimeEnv; using ::cel::expr::Expr; using ::google::api::expr::parser::Parse; using ::testing::Eq; @@ -63,7 +63,7 @@ class ShortCircuitingTest cel::UnknownProcessingOptions::kAttributeAndFunction; } auto result = std::make_unique( - NewTestingRuntimeEnv(), options); + NewTestingLegacyRuntimeEnv(), options); ABSL_CHECK_OK(RegisterBuiltinFunctions(result->GetRegistry())); return result; } diff --git a/eval/compiler/flat_expr_builder_test.cc b/eval/compiler/flat_expr_builder_test.cc index 63c7c68cd..813c7d2b9 100644 --- a/eval/compiler/flat_expr_builder_test.cc +++ b/eval/compiler/flat_expr_builder_test.cc @@ -85,7 +85,8 @@ #include "parser/parser.h" #include "runtime/function.h" #include "runtime/function_adapter.h" -#include "runtime/internal/runtime_env_testing.h" +#include "runtime/internal/legacy_runtime_env.h" +#include "runtime/internal/legacy_runtime_env_testing.h" #include "runtime/runtime_options.h" #include "runtime/standard_functions.h" #include "google/protobuf/arena.h" @@ -104,7 +105,8 @@ using ::cel::BytesValue; using ::cel::Value; using ::cel::expr::conformance::proto3::TestAllTypes; using ::cel::internal::test::EqualsProto; -using ::cel::runtime_internal::NewTestingRuntimeEnv; +using ::cel::runtime_internal::LegacyRuntimeEnv; +using ::cel::runtime_internal::NewTestingLegacyRuntimeEnv; using ::cel::expr::CheckedExpr; using ::cel::expr::Expr; using ::cel::expr::ParsedExpr; @@ -173,7 +175,7 @@ TEST(FlatExprBuilderTest, SimpleEndToEnd) { auto arg2 = call_expr->add_args(); arg2->mutable_ident_expr()->set_name("value"); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT( builder.GetRegistry()->Register(std::make_unique()), @@ -196,7 +198,7 @@ TEST(FlatExprBuilderTest, SimpleEndToEnd) { TEST(FlatExprBuilderTest, ExprUnset) { Expr expr; SourceInfo source_info; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Invalid empty expression"))); @@ -210,7 +212,7 @@ TEST(FlatExprBuilderTest, RuntimeExtensionsError) { ext->add_affected_components( cel::expr::SourceInfo::Extension::COMPONENT_RUNTIME); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("unsupported CEL extension: ext1"))); @@ -230,7 +232,7 @@ TEST(FlatExprBuilderTest, SelectOptimizationRuntimeExtension) { return builder.CreateExpression(&expr, &source_info).status(); }; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); // Other program optimizers don't imply select optimization support. builder.flat_expr_builder().AddProgramOptimizer( [](auto&, const auto&) { return nullptr; }); @@ -264,7 +266,7 @@ TEST(FlatExprBuilderTest, SelectOptimizationRuntimeExtension) { TEST(FlatExprBuilderTest, ConstValueUnset) { Expr expr; SourceInfo source_info; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); // Create an empty constant expression to ensure that it triggers an error. expr.mutable_const_expr(); @@ -276,7 +278,7 @@ TEST(FlatExprBuilderTest, ConstValueUnset) { TEST(FlatExprBuilderTest, MapKeyValueUnset) { Expr expr; SourceInfo source_info; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); // Don't set either the key or the value for the map creation step. auto* entry = expr.mutable_struct_expr()->add_entries(); @@ -294,7 +296,7 @@ TEST(FlatExprBuilderTest, MapKeyValueUnset) { TEST(FlatExprBuilderTest, MessageFieldValueUnset) { Expr expr; SourceInfo source_info; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); // Don't set either the field or the value for the message creation step. auto* create_message = expr.mutable_struct_expr(); @@ -314,7 +316,7 @@ TEST(FlatExprBuilderTest, MessageFieldValueUnset) { TEST(FlatExprBuilderTest, BinaryCallTooManyArguments) { Expr expr; SourceInfo source_info; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); auto* call = expr.mutable_call_expr(); call->set_function(cel::builtin::kAnd); @@ -340,7 +342,7 @@ TEST(FlatExprBuilderTest, TernaryCallTooManyArguments) { { cel::RuntimeOptions options; options.short_circuiting = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -351,7 +353,7 @@ TEST(FlatExprBuilderTest, TernaryCallTooManyArguments) { { cel::RuntimeOptions options; options.short_circuiting = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -373,7 +375,7 @@ TEST(FlatExprBuilderTest, DelayedFunctionResolutionErrors) { cel::RuntimeOptions options; options.fail_on_warnings = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); std::vector warnings; // Concat function not registered. @@ -417,7 +419,7 @@ TEST(FlatExprBuilderTest, Shortcircuiting) { { cel::RuntimeOptions options; options.short_circuiting = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); auto builtin = RegisterBuiltinFunctions(builder.GetRegistry()); int count1 = 0; @@ -442,7 +444,7 @@ TEST(FlatExprBuilderTest, Shortcircuiting) { { cel::RuntimeOptions options; options.short_circuiting = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); auto builtin = RegisterBuiltinFunctions(builder.GetRegistry()); int count1 = 0; @@ -492,7 +494,7 @@ TEST(FlatExprBuilderTest, ShortcircuitingComprehension) { { cel::RuntimeOptions options; options.short_circuiting = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); auto builtin = RegisterBuiltinFunctions(builder.GetRegistry()); int count = 0; @@ -512,7 +514,7 @@ TEST(FlatExprBuilderTest, ShortcircuitingComprehension) { { cel::RuntimeOptions options; options.short_circuiting = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); auto builtin = RegisterBuiltinFunctions(builder.GetRegistry()); int count = 0; @@ -533,7 +535,7 @@ TEST(FlatExprBuilderTest, IdentExprUnsetName) { // An empty ident without the name set should error. ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(R"(ident_expr {})", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -549,7 +551,7 @@ TEST(FlatExprBuilderTest, SelectExprUnsetField) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -566,7 +568,7 @@ TEST(FlatExprBuilderTest, SelectExprUnsetOperand) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -579,7 +581,7 @@ TEST(FlatExprBuilderTest, ComprehensionExprUnsetAccuVar) { // An empty ident without the name set should error. ASSERT_TRUE( google::protobuf::TextFormat::ParseFromString(R"(comprehension_expr{})", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -594,7 +596,7 @@ TEST(FlatExprBuilderTest, ComprehensionExprUnsetIterVar) { comprehension_expr{accu_var: "a"} )", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -611,7 +613,7 @@ TEST(FlatExprBuilderTest, ComprehensionExprUnsetAccuInit) { iter_var: "b"} )", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -631,7 +633,7 @@ TEST(FlatExprBuilderTest, ComprehensionExprUnsetLoopCondition) { }} )", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -654,7 +656,7 @@ TEST(FlatExprBuilderTest, ComprehensionExprUnsetLoopStep) { }} )", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -680,7 +682,7 @@ TEST(FlatExprBuilderTest, ComprehensionExprUnsetResult) { }} )", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), StatusIs(absl::StatusCode::kInvalidArgument, @@ -730,7 +732,7 @@ TEST(FlatExprBuilderTest, MapComprehension) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -762,7 +764,7 @@ TEST(FlatExprBuilderTest, InvalidContainer) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); builder.set_container(".bad"); @@ -778,7 +780,7 @@ TEST(FlatExprBuilderTest, InvalidContainer) { TEST(FlatExprBuilderTest, ParsedNamespacedFunctionSupport) { ASSERT_OK_AND_ASSIGN(ParsedExpr expr, parser::Parse("ext.XOr(a, b)")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kAlways)); using FunctionAdapterT = FunctionAdapter; @@ -808,7 +810,7 @@ TEST(FlatExprBuilderTest, ParsedNamespacedFunctionSupport) { TEST(FlatExprBuilderTest, ParsedNamespacedFunctionSupportWithContainer) { ASSERT_OK_AND_ASSIGN(ParsedExpr expr, parser::Parse("XOr(a, b)")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kAlways)); builder.set_container("ext"); @@ -838,7 +840,7 @@ TEST(FlatExprBuilderTest, ParsedNamespacedFunctionSupportWithContainer) { TEST(FlatExprBuilderTest, ParsedNamespacedFunctionResolutionOrder) { ASSERT_OK_AND_ASSIGN(ParsedExpr expr, parser::Parse("c.d.Get()")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kAlways)); builder.set_container("a.b"); @@ -865,7 +867,7 @@ TEST(FlatExprBuilderTest, ParsedNamespacedFunctionResolutionOrder) { TEST(FlatExprBuilderTest, ParsedNamespacedFunctionResolutionOrderParentContainer) { ASSERT_OK_AND_ASSIGN(ParsedExpr expr, parser::Parse("c.d.Get()")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kAlways)); builder.set_container("a.b"); @@ -892,7 +894,7 @@ TEST(FlatExprBuilderTest, TEST(FlatExprBuilderTest, ParsedNamespacedFunctionResolutionOrderExplicitGlobal) { ASSERT_OK_AND_ASSIGN(ParsedExpr expr, parser::Parse(".c.d.Get()")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kAlways)); builder.set_container("a.b"); @@ -918,7 +920,7 @@ TEST(FlatExprBuilderTest, TEST(FlatExprBuilderTest, ParsedNamespacedFunctionResolutionOrderReceiverCall) { ASSERT_OK_AND_ASSIGN(ParsedExpr expr, parser::Parse("e.Get()")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kAlways)); builder.set_container("a.b"); @@ -947,7 +949,7 @@ TEST(FlatExprBuilderTest, ParsedNamespacedFunctionSupportDisabled) { ASSERT_OK_AND_ASSIGN(ParsedExpr expr, parser::Parse("ext.XOr(a, b)")); cel::RuntimeOptions options; options.fail_on_warnings = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); std::vector build_warnings; builder.set_container("ext"); using FunctionAdapterT = FunctionAdapter; @@ -993,7 +995,7 @@ TEST(FlatExprBuilderTest, BasicCheckedExprSupport) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr)); @@ -1053,7 +1055,7 @@ TEST(FlatExprBuilderTest, CheckedExprWithReferenceMap) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kCheckedOnly)); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); @@ -1122,7 +1124,7 @@ TEST(FlatExprBuilderTest, CheckedExprWithReferenceMapFunction) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kCheckedOnly)); builder.set_container("com.foo"); @@ -1190,7 +1192,7 @@ TEST(FlatExprBuilderTest, CheckedExprActivationMissesReferences) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kCheckedOnly)); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); @@ -1255,7 +1257,7 @@ TEST(FlatExprBuilderTest, CheckedExprWithReferenceMapAndConstantFolding) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.flat_expr_builder().AddAstTransform( NewReferenceResolverExtension(ReferenceResolverOption::kCheckedOnly)); google::protobuf::Arena arena; @@ -1343,7 +1345,7 @@ TEST(FlatExprBuilderTest, ComprehensionWorksForError) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -1414,7 +1416,7 @@ TEST(FlatExprBuilderTest, ComprehensionWorksForNonContainer) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -1466,7 +1468,7 @@ TEST(FlatExprBuilderTest, ComprehensionBudget) { cel::RuntimeOptions options; options.comprehension_max_iterations = 1; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -1496,7 +1498,7 @@ TEST(FlatExprBuilderTest, SimpleEnumTest) { cur_expr->mutable_ident_expr()->set_name(enum_name_parts[0]); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.GetTypeRegistry()->Register(TestMessage::TestEnum_descriptor()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -1518,7 +1520,7 @@ TEST(FlatExprBuilderTest, SimpleEnumIdentTest) { Expr* cur_expr = &expr; cur_expr->mutable_ident_expr()->set_name(enum_name); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.GetTypeRegistry()->Register(TestMessage::TestEnum_descriptor()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -1536,18 +1538,18 @@ TEST(FlatExprBuilderTest, ContainerStringFormat) { expr.mutable_ident_expr()->set_name("ident"); { - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.set_container(""); ASSERT_THAT(builder.CreateExpression(&expr, &source_info), IsOk()); } { - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); builder.set_container("random.namespace"); ASSERT_THAT(builder.CreateExpression(&expr, &source_info), IsOk()); } { - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); // Leading '.' builder.set_container(".random.namespace"); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), @@ -1555,7 +1557,7 @@ TEST(FlatExprBuilderTest, ContainerStringFormat) { HasSubstr("Invalid expression container"))); } { - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); // Trailing '.' builder.set_container("random.namespace."); EXPECT_THAT(builder.CreateExpression(&expr, &source_info).status(), @@ -1571,7 +1573,7 @@ CelExpressionBuilderFlatImpl BuilderForNameResolutionTest( cel::RuntimeOptions options; options.enable_qualified_type_identifiers = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.GetTypeRegistry()->Register(TestMessage::TestEnum_descriptor()); builder.GetTypeRegistry()->Register(TestEnum_descriptor()); builder.set_container(std::string(container)); @@ -1796,7 +1798,7 @@ TEST(FlatExprBuilderTest, MapFieldPresence) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -1840,7 +1842,7 @@ TEST(FlatExprBuilderTest, RepeatedFieldPresence) { })", &expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -1883,7 +1885,7 @@ absl::Status RunTernaryExpression(CelValue selector, CelValue value1, auto arg2 = call_expr->add_args(); arg2->mutable_ident_expr()->set_name("value2"); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); CEL_ASSIGN_OR_RETURN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -1911,7 +1913,7 @@ TEST(FlatExprBuilderTest, Ternary) { auto arg2 = call_expr->add_args(); arg2->mutable_ident_expr()->set_name("value1"); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); @@ -2021,7 +2023,7 @@ TEST(FlatExprBuilderTest, EmptyCallList) { SourceInfo source_info; auto call_expr = expr.mutable_call_expr(); call_expr->set_function(op); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); auto build = builder.CreateExpression(&expr, &source_info); ASSERT_FALSE(build.ok()); @@ -2035,7 +2037,7 @@ TEST(FlatExprBuilderTest, HeterogeneousListsAllowed) { parser::Parse("[17, 'seventeen']")); cel::RuntimeOptions options; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto expression, builder.CreateExpression(&parsed_expr.expr(), @@ -2065,7 +2067,7 @@ TEST(FlatExprBuilderTest, NullUnboxingEnabled) { parser::Parse("message.int32_wrapper_value")); cel::RuntimeOptions options; options.enable_empty_wrapper_null_unboxing = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto expression, builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info())); @@ -2086,7 +2088,7 @@ TEST(FlatExprBuilderTest, TypeResolve) { parser::Parse("type(message) == runtime.TestMessage")); cel::RuntimeOptions options; options.enable_qualified_type_identifiers = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.set_container("google.api.expr"); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(auto expression, @@ -2111,7 +2113,7 @@ TEST(FlatExprBuilderTest, FastEquality) { options.enable_fast_builtins = true; InterpreterOptions legacy_options; legacy_options.enable_fast_builtins = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry(), legacy_options), IsOk()); ASSERT_OK_AND_ASSIGN(auto expression, @@ -2138,7 +2140,7 @@ TEST(FlatExprBuilderTest, FastEqualityFiltersBadCalls) { options.enable_fast_builtins = true; InterpreterOptions legacy_options; legacy_options.enable_fast_builtins = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry(), legacy_options), IsOk()); ASSERT_THAT( @@ -2159,7 +2161,7 @@ TEST(FlatExprBuilderTest, FastInequalityFiltersBadCalls) { options.enable_fast_builtins = true; InterpreterOptions legacy_options; legacy_options.enable_fast_builtins = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry(), legacy_options), IsOk()); ASSERT_THAT( @@ -2180,7 +2182,7 @@ TEST(FlatExprBuilderTest, FastInFiltersBadCalls) { options.enable_fast_builtins = true; InterpreterOptions legacy_options; legacy_options.enable_fast_builtins = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry(), legacy_options), IsOk()); ASSERT_THAT( @@ -2201,7 +2203,7 @@ TEST(FlatExprBuilderTest, IndexFiltersBadCalls) { options.enable_fast_builtins = true; InterpreterOptions legacy_options; legacy_options.enable_fast_builtins = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry(), legacy_options), IsOk()); ASSERT_THAT( @@ -2228,7 +2230,7 @@ TEST(FlatExprBuilderTest, IndexWithTarget) { options.enable_fast_builtins = true; InterpreterOptions legacy_options; legacy_options.enable_fast_builtins = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry(), legacy_options), IsOk()); ASSERT_THAT( @@ -2247,7 +2249,7 @@ TEST(FlatExprBuilderTest, NotFiltersBadCalls) { options.enable_fast_builtins = true; InterpreterOptions legacy_options; legacy_options.enable_fast_builtins = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry(), legacy_options), IsOk()); ASSERT_THAT( @@ -2266,7 +2268,7 @@ TEST(FlatExprBuilderTest, NotStrictlyFalseFiltersBadCalls) { options.enable_fast_builtins = true; InterpreterOptions legacy_options; legacy_options.enable_fast_builtins = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry(), legacy_options), IsOk()); ASSERT_THAT( @@ -2283,7 +2285,7 @@ TEST(FlatExprBuilderTest, FastEqualityDisabledWithCustomEquality) { options.enable_fast_builtins = true; InterpreterOptions legacy_options; legacy_options.enable_fast_builtins = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry(), legacy_options), IsOk()); @@ -2315,7 +2317,7 @@ TEST(FlatExprBuilderTest, AnyPackingList) { parser::Parse("TestAllTypes{single_any: [1, 2, 3]}")); cel::RuntimeOptions options; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.set_container("cel.expr.conformance.proto3"); ASSERT_OK_AND_ASSIGN(auto expression, @@ -2346,7 +2348,7 @@ TEST(FlatExprBuilderTest, AnyPackingNestedNumbers) { parser::Parse("TestAllTypes{single_any: [1, 2.3]}")); cel::RuntimeOptions options; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.set_container("cel.expr.conformance.proto3"); ASSERT_OK_AND_ASSIGN(auto expression, @@ -2375,7 +2377,7 @@ TEST(FlatExprBuilderTest, AnyPackingInt) { parser::Parse("TestAllTypes{single_any: 1}")); cel::RuntimeOptions options; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.set_container("cel.expr.conformance.proto3"); ASSERT_OK_AND_ASSIGN(auto expression, @@ -2403,7 +2405,7 @@ TEST(FlatExprBuilderTest, AnyPackingMap) { parser::Parse("TestAllTypes{single_any: {'key': 'value'}}")); cel::RuntimeOptions options; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); builder.set_container("cel.expr.conformance.proto3"); ASSERT_OK_AND_ASSIGN(auto expression, @@ -2434,7 +2436,7 @@ TEST(FlatExprBuilderTest, NullUnboxingDisabled) { parser::Parse("message.int32_wrapper_value")); cel::RuntimeOptions options; options.enable_empty_wrapper_null_unboxing = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto expression, builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info())); @@ -2454,7 +2456,7 @@ TEST(FlatExprBuilderTest, HeterogeneousEqualityEnabled) { parser::Parse("{1: 2, 2u: 3}[1.0]")); cel::RuntimeOptions options; options.enable_heterogeneous_equality = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto expression, builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info())); @@ -2472,7 +2474,7 @@ TEST(FlatExprBuilderTest, HeterogeneousEqualityDisabled) { parser::Parse("{1: 2, 2u: 3}[1.0]")); cel::RuntimeOptions options; options.enable_heterogeneous_equality = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto expression, builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info())); @@ -2521,7 +2523,7 @@ TEST_P(CustomDescriptorPoolTest, TestType) { ASSERT_THAT(AddStandardMessageTypesToDescriptorPool(descriptor_pool), IsOk()); google::protobuf::DynamicMessageFactory message_factory(&descriptor_pool); ASSERT_OK_AND_ASSIGN(ParsedExpr parsed_expr, parser::Parse("m")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); // Create test subject, invoke custom setter for message @@ -2756,7 +2758,7 @@ TEST(FlatExprBuilderTest, BlockBadIndex) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("bad @index"))); @@ -2778,7 +2780,7 @@ TEST(FlatExprBuilderTest, OutOfRangeBlockIndex) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, @@ -2799,7 +2801,7 @@ TEST(FlatExprBuilderTest, EarlyBlockIndex) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, @@ -2814,7 +2816,7 @@ TEST(FlatExprBuilderTest, OutOfScopeCSE) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, @@ -2830,7 +2832,7 @@ TEST(FlatExprBuilderTest, BlockMissingBindings) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, @@ -2851,7 +2853,7 @@ TEST(FlatExprBuilderTest, BlockMissingExpression) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, @@ -2872,7 +2874,7 @@ TEST(FlatExprBuilderTest, BlockNotListOfBoundExpressions) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, @@ -2895,7 +2897,7 @@ TEST(FlatExprBuilderTest, BlockEmptyListOfBoundExpressions) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, @@ -2921,7 +2923,7 @@ TEST(FlatExprBuilderTest, BlockOptionalListOfBoundExpressions) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, @@ -2955,7 +2957,7 @@ TEST(FlatExprBuilderTest, BlockNested) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); EXPECT_THAT( builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info()), StatusIs(absl::StatusCode::kInvalidArgument, @@ -2986,7 +2988,7 @@ TEST_P(FlatExprBuilderVariadicLogicalTest, Evaluate) { cel::RuntimeOptions options; options.unknown_processing = cel::UnknownProcessingOptions::kAttributeAndFunction; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&parsed_expr.expr(), &parsed_expr.source_info())); @@ -3100,7 +3102,7 @@ void ReplaceResultAccumulatorWithLegacyName(Expr* expr) { TEST(FlatExprBuilderTest, LegacyAccumulatorReferenceDoesNotMutateInPlace) { cel::RuntimeOptions options; options.enable_comprehension_list_append = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); for (absl::string_view expr_str : { @@ -3151,7 +3153,7 @@ TEST(FlatExprBuilderTest, cel::RuntimeOptions options; options.enable_comprehension_list_append = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(std::unique_ptr cel_expr, @@ -3167,7 +3169,7 @@ TEST(FlatExprBuilderTest, TEST(FlatExprBuilderTest, StackSizeEstimationBuiltinsAndGenericSteps) { cel::RuntimeOptions options; options.short_circuiting = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); { @@ -3225,7 +3227,7 @@ TEST(FlatExprBuilderTest, StackSizeEstimationBuiltinsAndGenericSteps) { TEST(FlatExprBuilderTest, StackSizeEstimationShortCircuiting) { cel::RuntimeOptions options; options.short_circuiting = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); { @@ -3309,7 +3311,7 @@ TEST(FlatExprBuilderTest, StackSizeEstimationWithLazySubexpressions) { )pb", &parsed_expr)); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, cel::extensions::CreateAstFromParsedExpr(parsed_expr)); @@ -3356,7 +3358,7 @@ TEST(FlatExprBuilderTest, StackSizeEstimationOverflowAndUnderflowFallback) { for (int64_t bad_delta : {static_cast(std::numeric_limits::max()), static_cast(-10)}) { - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); builder.flat_expr_builder().AddProgramOptimizer( [bad_delta](PlannerContext&, const cel::Ast&) @@ -3410,7 +3412,7 @@ TEST(FlatExprBuilderTest, ListAppendComprehensionWithTargetFailsPlanning) { cel::RuntimeOptions options; options.enable_comprehension_list_append = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); EXPECT_THAT( @@ -3419,10 +3421,10 @@ TEST(FlatExprBuilderTest, ListAppendComprehensionWithTargetFailsPlanning) { } TEST(FlatExprBuilderTest, ProgramOptimizerResolvesTypesWhenRequested) { - std::shared_ptr env = - NewTestingRuntimeEnv(); - ASSERT_OK_AND_ASSIGN(std::unique_ptr compiler_builder, - cel::NewCompilerBuilder(env->descriptor_pool)); + std::shared_ptr env = NewTestingLegacyRuntimeEnv(); + ASSERT_OK_AND_ASSIGN( + std::unique_ptr compiler_builder, + cel::NewCompilerBuilder(env->runtime_env.descriptor_pool)); ASSERT_THAT(compiler_builder->AddLibrary(cel::StandardCompilerLibrary()), IsOk()); ASSERT_THAT(compiler_builder->GetCheckerBuilder().AddVariable( @@ -3532,7 +3534,7 @@ class CountingActivation : public BaseActivation { TEST(FlatExprBuilderTest, IdentifierTableRepeatedRootIdentifierReceivesSlot) { ASSERT_OK_AND_ASSIGN(ParsedExpr parsed_expr, parser::Parse("x + y + x")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(std::unique_ptr cel_expr, builder.CreateExpression(&parsed_expr.expr(), @@ -3564,7 +3566,7 @@ TEST(FlatExprBuilderTest, ASSERT_OK_AND_ASSIGN( ParsedExpr parsed_expr, parser::Parse("[1, 2, 3].all(i, i < limit) && single_var")); - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv()); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv()); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(std::unique_ptr cel_expr, builder.CreateExpression(&parsed_expr.expr(), @@ -3667,7 +3669,7 @@ TEST(FlatExprBuilderTest, BlockInlinesTrivialSubexpressionsEnabled) { cel::RuntimeOptions options; options.enable_subexpression_inlining = true; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), absl_testing::IsOk()); ASSERT_OK_AND_ASSIGN(std::unique_ptr cel_expr, @@ -3712,7 +3714,7 @@ TEST(FlatExprBuilderTest, BlockInlinesTrivialSubexpressionsDisabled) { cel::RuntimeOptions options; options.enable_subexpression_inlining = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), absl_testing::IsOk()); ASSERT_OK_AND_ASSIGN(std::unique_ptr cel_expr, @@ -3843,6 +3845,198 @@ TEST(FlatExprBuilderTest, BindInlinesConstantAndFoldsMainExpression) { ASSERT_OK_AND_ASSIGN(CelValue result, cel_expr->Evaluate(activation, &arena)); EXPECT_THAT(result, test::IsCelInt64(16)); } + +class OptionalWrapFunction final : public cel::Function { + public: + explicit OptionalWrapFunction(bool empty) : empty_(empty) {} + + absl::StatusOr Invoke( + absl::Span args, + const InvokeContext& context) const override { + if (empty_) { + return cel::OptionalValue::None(); + } + return cel::OptionalValue::Of(args[0], context.arena()); + } + + private: + bool empty_; +}; + +class OptionalUnwrapOrSentinelFunction final : public cel::Function { + public: + absl::StatusOr Invoke( + absl::Span args, + const InvokeContext& context) const override { + if (!args[0].IsOptional()) { + return args[0]; + } + const cel::OptionalValue& opt = args[0].GetOptional(); + if (!opt.HasValue()) { + return cel::StringValue::Literal("optional.none"); + } + return opt.Value(); + } +}; + +TEST(FlatExprBuilderTest, ChainedTypedSelectOptionalAndHas) { + std::shared_ptr env = NewTestingLegacyRuntimeEnv(); + cel::RuntimeOptions options; + options.enable_typed_field_access = true; + CelExpressionBuilderFlatImpl builder(env, options); + ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); + ASSERT_THAT(builder.GetRegistry()->Register( + cel::FunctionDescriptor("make_opt", false, {cel::Kind::kAny}), + std::make_unique(/*empty=*/false)), + IsOk()); + ASSERT_THAT(builder.GetRegistry()->Register( + cel::FunctionDescriptor("make_none", false, {}), + std::make_unique(/*empty=*/true)), + IsOk()); + ASSERT_THAT( + builder.GetRegistry()->Register( + cel::FunctionDescriptor("unwrap_opt", false, {cel::Kind::kAny}), + std::make_unique()), + IsOk()); + builder.flat_expr_builder().enable_optional_types(); + + auto make_optional_msg_type = [](std::string msg_name) { + return cel::TypeSpec(cel::AbstractType( + "optional_type", + {cel::TypeSpec(cel::MessageTypeSpec(std::move(msg_name)))})); + }; + + auto build_select_chain = + [&](bool start_with_none, + bool test_only) -> absl::StatusOr { + cel::Expr ident_or_call; + if (start_with_none) { + ident_or_call.set_id(1); + ident_or_call.mutable_call_expr().set_function("make_none"); + } else { + ident_or_call.set_id(1); + auto& call = ident_or_call.mutable_call_expr(); + call.set_function("make_opt"); + auto& arg = call.mutable_args().emplace_back(); + arg.set_id(10); + arg.mutable_ident_expr().set_name("message"); + } + + cel::Expr select1; + select1.set_id(2); + select1.mutable_select_expr().set_field("single_nested_message"); + select1.mutable_select_expr().set_operand(std::move(ident_or_call)); + + cel::Expr select2; + select2.set_id(3); + select2.mutable_select_expr().set_field("bb"); + select2.mutable_select_expr().set_test_only(test_only); + select2.mutable_select_expr().set_operand(std::move(select1)); + + cel::Expr root; + if (test_only) { + root = std::move(select2); + } else { + root.set_id(4); + root.mutable_call_expr().set_function("unwrap_opt"); + root.mutable_call_expr().mutable_args().push_back(std::move(select2)); + } + + cel::Ast::TypeMap type_map; + type_map[1] = + make_optional_msg_type("cel.expr.conformance.proto3.TestAllTypes"); + type_map[2] = make_optional_msg_type( + "cel.expr.conformance.proto3.TestAllTypes.NestedMessage"); + if (!start_with_none) { + type_map[10] = cel::TypeSpec( + cel::MessageTypeSpec("cel.expr.conformance.proto3.TestAllTypes")); + } + if (test_only) { + type_map[3] = cel::TypeSpec(cel::PrimitiveType::kBool); + } else { + type_map[3] = cel::TypeSpec(cel::AbstractType( + "optional_type", {cel::TypeSpec(cel::PrimitiveType::kInt64)})); + } + + auto ast = std::make_unique(std::move(root), cel::SourceInfo(), + cel::Ast::ReferenceMap(), + std::move(type_map), "0"); + return builder.flat_expr_builder().CreateExpressionImpl(std::move(ast), + nullptr); + }; + + google::protobuf::Arena arena; + { + ASSERT_OK_AND_ASSIGN(FlatExpression flat_expr, + build_select_chain(/*start_with_none=*/false, + /*test_only=*/false)); + CelExpressionFlatImpl cel_expr(env->GetRuntimeEnv(), std::move(flat_expr)); + + cel::expr::conformance::proto3::TestAllTypes message; + { + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena)); + EXPECT_THAT(result, test::IsCelString("optional.none")); + } + message.mutable_single_nested_message()->set_bb(55); + { + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena)); + EXPECT_THAT(result, test::IsCelInt64(55)); + } + } + { + ASSERT_OK_AND_ASSIGN(FlatExpression flat_expr, + build_select_chain(/*start_with_none=*/true, + /*test_only=*/false)); + CelExpressionFlatImpl cel_expr(env->GetRuntimeEnv(), std::move(flat_expr)); + Activation activation; + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena)); + EXPECT_THAT(result, test::IsCelString("optional.none")); + } + { + ASSERT_OK_AND_ASSIGN(FlatExpression flat_expr, + build_select_chain(/*start_with_none=*/false, + /*test_only=*/true)); + CelExpressionFlatImpl cel_expr(env->GetRuntimeEnv(), std::move(flat_expr)); + + cel::expr::conformance::proto3::TestAllTypes message; + { + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena)); + EXPECT_THAT(result, test::IsCelBool(false)); + } + message.mutable_single_nested_message()->set_bb(55); + { + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena)); + EXPECT_THAT(result, test::IsCelBool(true)); + } + } + { + ASSERT_OK_AND_ASSIGN(FlatExpression flat_expr, + build_select_chain(/*start_with_none=*/true, + /*test_only=*/true)); + CelExpressionFlatImpl cel_expr(env->GetRuntimeEnv(), std::move(flat_expr)); + Activation activation; + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena)); + EXPECT_THAT(result, test::IsCelBool(false)); + } +} } // namespace } // namespace google::api::expr::runtime diff --git a/eval/compiler/regex_precompilation_optimization_test.cc b/eval/compiler/regex_precompilation_optimization_test.cc index 9666144b2..4ddc60ff5 100644 --- a/eval/compiler/regex_precompilation_optimization_test.cc +++ b/eval/compiler/regex_precompilation_optimization_test.cc @@ -40,8 +40,8 @@ #include "internal/testing.h" #include "parser/parser.h" #include "runtime/internal/issue_collector.h" -#include "runtime/internal/runtime_env.h" -#include "runtime/internal/runtime_env_testing.h" +#include "runtime/internal/legacy_runtime_env.h" +#include "runtime/internal/legacy_runtime_env_testing.h" #include "runtime/runtime_issue.h" #include "runtime/runtime_options.h" #include "google/protobuf/arena.h" @@ -51,8 +51,8 @@ namespace { using ::cel::RuntimeIssue; using ::cel::runtime_internal::IssueCollector; -using ::cel::runtime_internal::NewTestingRuntimeEnv; -using ::cel::runtime_internal::RuntimeEnv; +using ::cel::runtime_internal::LegacyRuntimeEnv; +using ::cel::runtime_internal::NewTestingLegacyRuntimeEnv; using ::google::api::expr::parser::Parse; using ::testing::ElementsAre; @@ -61,7 +61,7 @@ namespace exprpb = cel::expr; class RegexPrecompilationExtensionTest : public testing::TestWithParam { public: RegexPrecompilationExtensionTest() - : env_(NewTestingRuntimeEnv()), + : env_(NewTestingLegacyRuntimeEnv()), builder_(env_), type_registry_(*builder_.GetTypeRegistry()), function_registry_(*builder_.GetRegistry()), @@ -95,7 +95,7 @@ class RegexPrecompilationExtensionTest : public testing::TestWithParam { }; } - absl_nonnull std::shared_ptr env_; + absl_nonnull std::shared_ptr env_; CelExpressionBuilderFlatImpl builder_; CelTypeRegistry& type_registry_; CelFunctionRegistry& function_registry_; @@ -114,7 +114,7 @@ TEST_P(RegexPrecompilationExtensionTest, SmokeTest) { cel::Ast ast_impl; ast_impl.set_is_checked(true); std::shared_ptr arena; - PlannerContext context(env_, resolver_, runtime_options_, + PlannerContext context(env_->GetRuntimeEnv(), resolver_, runtime_options_, type_registry_.GetTypeProvider(), issue_collector_, program_builder, arena); diff --git a/eval/eval/BUILD b/eval/eval/BUILD index c19252d02..d1b36c477 100644 --- a/eval/eval/BUILD +++ b/eval/eval/BUILD @@ -295,7 +295,6 @@ cc_library( ":expression_step_base", ":expression_step_logic", "//base:attributes", - "//common:legacy_value", "//common:type", "//common:value", "//common:value_kind", @@ -496,6 +495,7 @@ cc_test( "//runtime:function_overload_reference", "//runtime:function_registry", "//runtime:runtime_options", + "//runtime/internal:legacy_runtime_env_testing", "//runtime/internal:runtime_env_testing", "//runtime/internal:runtime_type_provider", "@com_google_absl//absl/container:flat_hash_set", diff --git a/eval/eval/create_struct_step_test.cc b/eval/eval/create_struct_step_test.cc index 555f22376..5483a192f 100644 --- a/eval/eval/create_struct_step_test.cc +++ b/eval/eval/create_struct_step_test.cc @@ -168,7 +168,9 @@ class CreateCreateStructStepTest TEST_P(CreateCreateStructStepTest, TestEmptyMessageCreation) { ExecutionPath path; - auto adapter = env_->legacy_type_registry.FindTypeAdapter( + CelTypeRegistry legacy_type_registry(env_->descriptor_pool.get(), + env_->MutableMessageFactory()); + auto adapter = legacy_type_registry.FindTypeAdapter( "google.api.expr.runtime.TestMessage"); ASSERT_TRUE(adapter.has_value() && adapter->access_apis() != nullptr); diff --git a/eval/eval/evaluator_core_test.cc b/eval/eval/evaluator_core_test.cc index 4ee559441..63261c72e 100644 --- a/eval/eval/evaluator_core_test.cc +++ b/eval/eval/evaluator_core_test.cc @@ -28,6 +28,7 @@ #include "runtime/activation.h" #include "runtime/function_overload_reference.h" #include "runtime/function_registry.h" +#include "runtime/internal/legacy_runtime_env_testing.h" #include "runtime/internal/runtime_env_testing.h" #include "runtime/internal/runtime_type_provider.h" #include "runtime/runtime_options.h" @@ -40,6 +41,7 @@ using ::absl_testing::StatusIs; using ::cel::IntValue; using ::cel::TypeProvider; using ::cel::interop_internal::CreateIntValue; +using ::cel::runtime_internal::NewTestingLegacyRuntimeEnv; using ::cel::runtime_internal::NewTestingRuntimeEnv; using ::cel::expr::Expr; using ::google::api::expr::runtime::RegisterBuiltinFunctions; @@ -370,7 +372,7 @@ TEST(EvaluatorCoreTest, TraceTest) { cel::RuntimeOptions options; options.short_circuiting = false; - CelExpressionBuilderFlatImpl builder(NewTestingRuntimeEnv(), options); + CelExpressionBuilderFlatImpl builder(NewTestingLegacyRuntimeEnv(), options); ASSERT_THAT(RegisterBuiltinFunctions(builder.GetRegistry()), IsOk()); ASSERT_OK_AND_ASSIGN(auto cel_expr, builder.CreateExpression(&expr, &source_info)); diff --git a/eval/eval/select_step.cc b/eval/eval/select_step.cc index 8a70e3b82..9fb42fa5a 100644 --- a/eval/eval/select_step.cc +++ b/eval/eval/select_step.cc @@ -11,7 +11,6 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "base/attribute.h" -#include "common/legacy_value.h" #include "common/type.h" #include "common/value.h" #include "common/value_kind.h" @@ -281,6 +280,52 @@ bool SupportsCachedFieldDescriptor( return true; } +// Attempts to unwrap `arg` into a `ParsedMessageValue` matching `descriptor` +// and `field_descriptor`. Populates `legacy_struct` when `arg` is a legacy +// `CelValue` message wrapper so field wrapping preserves legacy interop. +absl::optional TryUnwrapTypedMessage( + const Value& arg, const google::protobuf::Descriptor* descriptor, + const google::protobuf::FieldDescriptor* field_descriptor, + absl::optional& legacy_struct) { + if (auto unwrapped = arg.AsParsedMessage(); + unwrapped.has_value() && + SupportsCachedFieldDescriptor(*unwrapped, descriptor, field_descriptor)) { + legacy_struct = absl::nullopt; + return *unwrapped; + } + if (auto legacy = cel::common_internal::AsLegacyStructValue(arg); + legacy.has_value()) { + if (const google::protobuf::Message* legacy_message = legacy->GetLegacyMessage(); + legacy_message != nullptr) { + cel::ParsedMessageValue parsed_message = + cel::UnsafeParsedMessageValue(legacy_message); + if (SupportsCachedFieldDescriptor(parsed_message, descriptor, + field_descriptor)) { + legacy_struct = *legacy; + return parsed_message; + } + } + } + return absl::nullopt; +} + +absl::Status GetTypedMessageField( + const cel::ParsedMessageValue& parsed_message, + const absl::optional& + legacy_struct, + const google::protobuf::FieldDescriptor* field_descriptor, + ProtoWrapperTypeOptions unboxing_option, ExecutionFrame* frame, + Value* result) { + if (legacy_struct.has_value()) { + return legacy_struct->WrapLegacyMessageField( + field_descriptor, unboxing_option, frame->descriptor_pool(), + frame->message_factory(), frame->arena(), result); + } + return parsed_message.GetField( + field_descriptor, unboxing_option, frame->descriptor_pool(), + frame->message_factory(), frame->arena(), result); +} + class ProtoSelectStep : public SelectStep { public: ProtoSelectStep(absl::string_view value, @@ -304,72 +349,68 @@ class ProtoSelectStep : public SelectStep { } const Value& arg = frame->value_stack().Peek(); - if (auto unwrapped = arg.AsParsedMessage(); - unwrapped.has_value() && - SupportsCachedFieldDescriptor(*unwrapped, descriptor_, - field_descriptor_)) { - EvaluateMessageFieldGet(frame, *unwrapped); + absl::optional legacy_struct; + if (auto parsed_message = TryUnwrapTypedMessage( + arg, descriptor_, field_descriptor_, legacy_struct); + parsed_message.has_value()) { + if (CheckAttributeTrail(field_, frame)) { + return; + } + if (absl::Status status = GetTypedMessageField( + *parsed_message, legacy_struct, field_descriptor_, + unboxing_option_, frame, &frame->value_stack().Peek()); + !status.ok()) { + frame->Abort(std::move(status)); + } return; - } else if (const google::protobuf::Message* legacy_message = - cel::interop_internal::GetLegacyMessage(arg); - legacy_message != nullptr) { - auto parsed_message = cel::UnsafeParsedMessageValue(legacy_message); - // A little unfortunate, but need to special case for legacy values so we - // can minimize back and forth interop conversions. - if (SupportsCachedFieldDescriptor(parsed_message, descriptor_, - field_descriptor_)) { - EvaluateMessageFieldGet(frame, legacy_message); + } + + if (enable_optional_types_ && arg.IsOptional()) { + const OptionalValue& optional_arg = arg.GetOptional(); + if (!optional_arg.HasValue()) { + if (CheckAttributeTrail(field_, frame)) { + return; + } + frame->value_stack().Peek() = OptionalValue::None(); + return; + } + Value unwrapped_opt; + optional_arg.Value(&unwrapped_opt); + if (auto parsed_message = TryUnwrapTypedMessage( + unwrapped_opt, descriptor_, field_descriptor_, legacy_struct); + parsed_message.has_value()) { + if (CheckAttributeTrail(field_, frame)) { + return; + } + if (!parsed_message->HasField(field_descriptor_)) { + frame->value_stack().Peek() = OptionalValue::None(); + return; + } + Value field_result; + if (absl::Status status = GetTypedMessageField( + *parsed_message, legacy_struct, field_descriptor_, + unboxing_option_, frame, &field_result); + !status.ok()) { + frame->Abort(std::move(status)); + return; + } + ABSL_DCHECK(!field_result.IsUnknown()); + frame->value_stack().Peek() = + OptionalValue::Of(std::move(field_result), frame->arena()); return; } } + // If we get an unexpected value type, fall back to the generic // implementation. SelectStep::Evaluate(frame); } private: - void EvaluateMessageFieldGet( - ExecutionFrame* frame, - const cel::ParsedMessageValue& parsed_message) const; - void EvaluateMessageFieldGet( - ExecutionFrame* frame, - const google::protobuf::Message* absl_nonnull legacy_message) const; - const google::protobuf::Descriptor* descriptor_; const google::protobuf::FieldDescriptor* field_descriptor_; }; -void ProtoSelectStep::EvaluateMessageFieldGet( - ExecutionFrame* frame, - const cel::ParsedMessageValue& parsed_message) const { - if (CheckAttributeTrail(field_, frame)) { - return; - } - if (absl::Status status = parsed_message.GetField( - field_descriptor_, unboxing_option_, frame->descriptor_pool(), - frame->message_factory(), frame->arena(), - &frame->value_stack().Peek()); - !status.ok()) { - frame->Abort(std::move(status)); - } -} - -void ProtoSelectStep::EvaluateMessageFieldGet( - ExecutionFrame* frame, - const google::protobuf::Message* absl_nonnull legacy_message) const { - ABSL_DCHECK(legacy_message != nullptr); - if (CheckAttributeTrail(field_, frame)) { - return; - } - if (absl::Status status = cel::interop_internal::WrapLegacyMessageField( - legacy_message, field_descriptor_, unboxing_option_, - frame->descriptor_pool(), frame->message_factory(), frame->arena(), - &frame->value_stack().Peek()); - !status.ok()) { - frame->Abort(std::move(status)); - } -} - class ProtoHasStep : public SelectStep { public: ProtoHasStep(absl::string_view field, bool enable_wrapper_type_null_unboxing, @@ -391,23 +432,33 @@ class ProtoHasStep : public SelectStep { } const Value& arg = frame->value_stack().Peek(); - if (auto unwrapped = arg.AsParsedMessage(); - unwrapped.has_value() && - SupportsCachedFieldDescriptor(*unwrapped, descriptor_, - field_descriptor_)) { - EvaluateHas(frame, *unwrapped); + absl::optional legacy_struct; + if (auto parsed_message = TryUnwrapTypedMessage( + arg, descriptor_, field_descriptor_, legacy_struct); + parsed_message.has_value()) { + EvaluateHas(frame, *parsed_message); return; - } else if (const google::protobuf::Message* legacy_message = - cel::interop_internal::GetLegacyMessage(arg); - legacy_message != nullptr) { - cel::ParsedMessageValue parsed_message = - cel::UnsafeParsedMessageValue(legacy_message); - if (SupportsCachedFieldDescriptor(parsed_message, descriptor_, - field_descriptor_)) { - EvaluateHas(frame, parsed_message); + } + + if (enable_optional_types_ && arg.IsOptional()) { + const OptionalValue& optional_arg = arg.GetOptional(); + if (!optional_arg.HasValue()) { + if (CheckAttributeTrail(field_, frame)) { + return; + } + frame->value_stack().Peek() = cel::FalseValue(); + return; + } + Value unwrapped_opt; + optional_arg.Value(&unwrapped_opt); + if (auto parsed_message = TryUnwrapTypedMessage( + unwrapped_opt, descriptor_, field_descriptor_, legacy_struct); + parsed_message.has_value()) { + EvaluateHas(frame, *parsed_message); return; } } + // If we get an unexpected value type, fall back to the generic // implementation. SelectStep::Evaluate(frame); @@ -447,17 +498,15 @@ absl::StatusOr> CreateTypedSelectStep( absl::string_view field, cel::StructType resolved_operand_type, cel::StructTypeField resolved_field, bool test_only, bool enable_wrapper_type_null_unboxing, bool enable_optional_types) { - if (!resolved_operand_type.IsMessage()) { + if (!resolved_operand_type.IsMessage() || !resolved_field.IsMessage()) { // The specialization only supports messages. Fallback to the generic // implementation for other types. - // TODO(uncreated-issue/89): support optional select and chaining. return CreateSelectStep(field, test_only, enable_wrapper_type_null_unboxing, enable_optional_types); } const google::protobuf::Descriptor* descriptor = resolved_operand_type.GetMessage().descriptor(); - ABSL_DCHECK(resolved_field.IsMessage()); const google::protobuf::FieldDescriptor* field_descriptor = resolved_field.GetMessage().descriptor(); diff --git a/eval/eval/select_step_test.cc b/eval/eval/select_step_test.cc index 8a89529b5..29ac2f388 100644 --- a/eval/eval/select_step_test.cc +++ b/eval/eval/select_step_test.cc @@ -20,6 +20,7 @@ #include "common/expr.h" #include "common/legacy_value.h" #include "common/type.h" +#include "common/value.h" #include "eval/eval/cel_expression_flat_impl.h" #include "eval/eval/evaluator_core.h" #include "eval/public/activation.h" @@ -1053,6 +1054,389 @@ TEST_F(SelectStepTest, TypedSelectStepUnknownPatternResolvesToUnknown) { ASSERT_TRUE(result.IsUnknownSet()); } +// Helper step to wrap the top-of-stack value in `cel::OptionalValue::Of(...)` +// while preserving its `AttributeTrail` for unknown attribute tests. +class WrapOptionalTestStep : public ExpressionStepLogic { + public: + void Evaluate(ExecutionFrame* frame) const override { + cel::Value& top = frame->value_stack().Peek(); + top = cel::OptionalValue::Of(std::move(top), frame->arena()); + } +}; + +// Helper step to unwrap `cel::OptionalValue` into a sentinel `StringValue` +// ("optional.none") or its held value so `CelExpressionFlatImpl::Evaluate` can +// return it as a `CelValue`. +class UnwrapOptionalTestStep : public ExpressionStepLogic { + public: + void Evaluate(ExecutionFrame* frame) const override { + cel::Value& top = frame->value_stack().Peek(); + if (!top.IsOptional()) { + return; + } + const cel::OptionalValue& opt = top.GetOptional(); + if (!opt.HasValue()) { + top = cel::StringValue::Literal("optional.none"); + return; + } + cel::Value inner; + opt.Value(&inner); + top = std::move(inner); + } +}; + +TEST_P(SelectStepConformanceTest, TypedHasStepTest) { + cel::StructType resolved_operand_type( + (cel::MessageType(TestAllTypes::descriptor()))); + const google::protobuf::FieldDescriptor* field_desc = + TestAllTypes::descriptor()->FindFieldByName("single_int64"); + ASSERT_NE(field_desc, nullptr); + cel::StructTypeField resolved_field((cel::MessageTypeField(field_desc))); + + ASSERT_OK_AND_ASSIGN( + auto step1, CreateTypedSelectStep( + "single_int64", resolved_operand_type, resolved_field, + /*test_only=*/true, + /*enable_wrapper_type_null_unboxing=*/false, + /*enable_optional_types=*/false)); + + ExecutionPath path; + path.push_back(ExpressionStep::MakeIdentifierStep(0)); + path.push_back(ExpressionStep::MakeGenericStep(std::move(step1))); + cel::RuntimeOptions options; + if (GetParam()) { + options.unknown_processing = cel::UnknownProcessingOptions::kAttributeOnly; + } + CelExpressionFlatImpl cel_expr( + env_, + FlatExpression(std::move(path), /*comprehension_slot_count=*/1, + env_->type_registry.GetComposedTypeProvider(), options, + /*arena=*/nullptr, IdentifierTable{{"message", 0}})); + + TestAllTypes message; + { + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena_)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsBool()); + EXPECT_FALSE(result.BoolOrDie()); + } + message.set_single_int64(42); + { + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena_)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsBool()); + EXPECT_TRUE(result.BoolOrDie()); + } +} + +TEST_P(SelectStepConformanceTest, ChainedTypedSelectStepTest) { + cel::StructType root_type((cel::MessageType(TestAllTypes::descriptor()))); + const google::protobuf::FieldDescriptor* nested_field_desc = + TestAllTypes::descriptor()->FindFieldByName("single_nested_message"); + ASSERT_NE(nested_field_desc, nullptr); + cel::StructTypeField nested_field((cel::MessageTypeField(nested_field_desc))); + + cel::StructType nested_type( + (cel::MessageType(TestAllTypes::NestedMessage::descriptor()))); + const google::protobuf::FieldDescriptor* bb_field_desc = + TestAllTypes::NestedMessage::descriptor()->FindFieldByName("bb"); + ASSERT_NE(bb_field_desc, nullptr); + cel::StructTypeField bb_field((cel::MessageTypeField(bb_field_desc))); + + ASSERT_OK_AND_ASSIGN( + auto step1, + CreateTypedSelectStep("single_nested_message", root_type, nested_field, + /*test_only=*/false, + /*enable_wrapper_type_null_unboxing=*/false, + /*enable_optional_types=*/false)); + ASSERT_OK_AND_ASSIGN( + auto step2, + CreateTypedSelectStep("bb", nested_type, bb_field, + /*test_only=*/false, + /*enable_wrapper_type_null_unboxing=*/false, + /*enable_optional_types=*/false)); + + ExecutionPath path; + path.push_back(ExpressionStep::MakeIdentifierStep(0)); + path.push_back(ExpressionStep::MakeGenericStep(std::move(step1))); + path.push_back(ExpressionStep::MakeGenericStep(std::move(step2))); + cel::RuntimeOptions options; + if (GetParam()) { + options.unknown_processing = cel::UnknownProcessingOptions::kAttributeOnly; + } + CelExpressionFlatImpl cel_expr( + env_, + FlatExpression(std::move(path), /*comprehension_slot_count=*/1, + env_->type_registry.GetComposedTypeProvider(), options, + /*arena=*/nullptr, IdentifierTable{{"message", 0}})); + + TestAllTypes message; + message.mutable_single_nested_message()->set_bb(99); + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena_)); + + ASSERT_OK_AND_ASSIGN(CelValue result, cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsInt64()); + EXPECT_EQ(result.Int64OrDie(), 99); +} + +TEST_P(SelectStepConformanceTest, TypedOptionalSelectStepTest) { + cel::StructType root_type((cel::MessageType(TestAllTypes::descriptor()))); + const google::protobuf::FieldDescriptor* nested_field_desc = + TestAllTypes::descriptor()->FindFieldByName("single_nested_message"); + ASSERT_NE(nested_field_desc, nullptr); + cel::StructTypeField nested_field((cel::MessageTypeField(nested_field_desc))); + + cel::StructType nested_type( + (cel::MessageType(TestAllTypes::NestedMessage::descriptor()))); + const google::protobuf::FieldDescriptor* bb_field_desc = + TestAllTypes::NestedMessage::descriptor()->FindFieldByName("bb"); + ASSERT_NE(bb_field_desc, nullptr); + cel::StructTypeField bb_field((cel::MessageTypeField(bb_field_desc))); + + auto make_chained_expr = + [&](bool start_with_none) -> absl::StatusOr { + ExecutionPath path; + if (start_with_none) { + path.push_back(ExpressionStep::MakeConstant(cel::OptionalValue::None())); + } else { + path.push_back(ExpressionStep::MakeIdentifierStep(0)); + path.push_back(ExpressionStep::MakeGenericStep( + std::make_unique(), -1, /*stack_delta=*/0)); + } + CEL_ASSIGN_OR_RETURN( + auto step1, + CreateTypedSelectStep("single_nested_message", root_type, nested_field, + /*test_only=*/false, + /*enable_wrapper_type_null_unboxing=*/false, + /*enable_optional_types=*/true)); + CEL_ASSIGN_OR_RETURN( + auto step2, + CreateTypedSelectStep("bb", nested_type, bb_field, /*test_only=*/false, + /*enable_wrapper_type_null_unboxing=*/false, + /*enable_optional_types=*/true)); + path.push_back(ExpressionStep::MakeGenericStep(std::move(step1))); + path.push_back(ExpressionStep::MakeGenericStep(std::move(step2))); + path.push_back(ExpressionStep::MakeGenericStep( + std::make_unique(), -1, /*stack_delta=*/0)); + + cel::RuntimeOptions options; + if (GetParam()) { + options.unknown_processing = + cel::UnknownProcessingOptions::kAttributeOnly; + } + return CelExpressionFlatImpl( + env_, + FlatExpression(std::move(path), /*comprehension_slot_count=*/1, + env_->type_registry.GetComposedTypeProvider(), options, + /*arena=*/nullptr, IdentifierTable{{"message", 0}})); + }; + + // Optional none propagates as optional.none. + { + ASSERT_OK_AND_ASSIGN(CelExpressionFlatImpl none_expr, + make_chained_expr(true)); + Activation activation; + ASSERT_OK_AND_ASSIGN(CelValue result, + none_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsString()); + EXPECT_EQ(result.StringOrDie().value(), "optional.none"); + } + + ASSERT_OK_AND_ASSIGN(CelExpressionFlatImpl cel_expr, + make_chained_expr(false)); + // Unset intermediate field returns optional.none. + { + TestAllTypes message; + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena_)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsString()); + EXPECT_EQ(result.StringOrDie().value(), "optional.none"); + } + // Present intermediate message with unset leaf field returns optional.none. + { + TestAllTypes message; + message.mutable_single_nested_message(); + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena_)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsString()); + EXPECT_EQ(result.StringOrDie().value(), "optional.none"); + } + // Present intermediate and leaf field returns optional.of(value). + { + TestAllTypes message; + message.mutable_single_nested_message()->set_bb(123); + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena_)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsInt64()); + EXPECT_EQ(result.Int64OrDie(), 123); + } + // Fallback when optional holds a legacy map instead of a proto message. + { + std::string nested_key = "single_nested_message"; + std::string bb_key = "bb"; + std::vector> inner_kvs{ + {CelValue::CreateString(&bb_key), CelValue::CreateInt64(77)}}; + ASSERT_OK_AND_ASSIGN(auto inner_map, + CreateContainerBackedMap(absl::MakeSpan(inner_kvs))); + std::vector> outer_kvs{ + {CelValue::CreateString(&nested_key), + CelValue::CreateMap(inner_map.get())}}; + ASSERT_OK_AND_ASSIGN(auto outer_map, + CreateContainerBackedMap(absl::MakeSpan(outer_kvs))); + Activation activation; + activation.InsertValue("message", CelValue::CreateMap(outer_map.get())); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsInt64()); + EXPECT_EQ(result.Int64OrDie(), 77); + } +} + +TEST_F(SelectStepTest, TypedOptionalSelectStepUnknownPatternResolvesToUnknown) { + cel::StructType root_type((cel::MessageType(TestAllTypes::descriptor()))); + const google::protobuf::FieldDescriptor* field_desc = + TestAllTypes::descriptor()->FindFieldByName("single_int64"); + ASSERT_NE(field_desc, nullptr); + cel::StructTypeField resolved_field((cel::MessageTypeField(field_desc))); + + ASSERT_OK_AND_ASSIGN( + auto step1, + CreateTypedSelectStep("single_int64", root_type, resolved_field, + /*test_only=*/false, + /*enable_wrapper_type_null_unboxing=*/false, + /*enable_optional_types=*/true)); + + ExecutionPath path; + path.push_back(ExpressionStep::MakeIdentifierStep(0)); + path.push_back(ExpressionStep::MakeGenericStep( + std::make_unique(), -1, /*stack_delta=*/0)); + path.push_back(ExpressionStep::MakeGenericStep(std::move(step1))); + path.push_back(ExpressionStep::MakeGenericStep( + std::make_unique(), -1, /*stack_delta=*/0)); + + cel::RuntimeOptions options; + options.unknown_processing = cel::UnknownProcessingOptions::kAttributeOnly; + CelExpressionFlatImpl cel_expr( + env_, + FlatExpression(std::move(path), /*comprehension_slot_count=*/1, + env_->type_registry.GetComposedTypeProvider(), options, + /*arena=*/nullptr, IdentifierTable{{"message", 0}})); + + TestAllTypes message; + message.set_single_int64(42); + std::vector unknown_patterns; + unknown_patterns.push_back(CelAttributePattern( + "message", {CelAttributeQualifierPattern::OfString("single_int64")})); + + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena_)); + ASSERT_THAT(activation.SetUnknownAttributePatterns(unknown_patterns), IsOk()); + + ASSERT_OK_AND_ASSIGN(CelValue result, cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsUnknownSet()); +} + +TEST_P(SelectStepConformanceTest, TypedOptionalHasStepTest) { + cel::StructType root_type((cel::MessageType(TestAllTypes::descriptor()))); + const google::protobuf::FieldDescriptor* nested_field_desc = + TestAllTypes::descriptor()->FindFieldByName("single_nested_message"); + ASSERT_NE(nested_field_desc, nullptr); + cel::StructTypeField nested_field((cel::MessageTypeField(nested_field_desc))); + + cel::StructType nested_type( + (cel::MessageType(TestAllTypes::NestedMessage::descriptor()))); + const google::protobuf::FieldDescriptor* bb_field_desc = + TestAllTypes::NestedMessage::descriptor()->FindFieldByName("bb"); + ASSERT_NE(bb_field_desc, nullptr); + cel::StructTypeField bb_field((cel::MessageTypeField(bb_field_desc))); + + auto make_has_expr = + [&](bool start_with_none) -> absl::StatusOr { + ExecutionPath path; + if (start_with_none) { + path.push_back(ExpressionStep::MakeConstant(cel::OptionalValue::None())); + } else { + path.push_back(ExpressionStep::MakeIdentifierStep(0)); + path.push_back(ExpressionStep::MakeGenericStep( + std::make_unique(), -1, /*stack_delta=*/0)); + } + CEL_ASSIGN_OR_RETURN( + auto step1, + CreateTypedSelectStep("single_nested_message", root_type, nested_field, + /*test_only=*/false, + /*enable_wrapper_type_null_unboxing=*/false, + /*enable_optional_types=*/true)); + CEL_ASSIGN_OR_RETURN( + auto step2, + CreateTypedSelectStep("bb", nested_type, bb_field, /*test_only=*/true, + /*enable_wrapper_type_null_unboxing=*/false, + /*enable_optional_types=*/true)); + path.push_back(ExpressionStep::MakeGenericStep(std::move(step1))); + path.push_back(ExpressionStep::MakeGenericStep(std::move(step2))); + + cel::RuntimeOptions options; + if (GetParam()) { + options.unknown_processing = + cel::UnknownProcessingOptions::kAttributeOnly; + } + return CelExpressionFlatImpl( + env_, + FlatExpression(std::move(path), /*comprehension_slot_count=*/1, + env_->type_registry.GetComposedTypeProvider(), options, + /*arena=*/nullptr, IdentifierTable{{"message", 0}})); + }; + + { + ASSERT_OK_AND_ASSIGN(CelExpressionFlatImpl none_expr, make_has_expr(true)); + Activation activation; + ASSERT_OK_AND_ASSIGN(CelValue result, + none_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsBool()); + EXPECT_FALSE(result.BoolOrDie()); + } + + ASSERT_OK_AND_ASSIGN(CelExpressionFlatImpl cel_expr, make_has_expr(false)); + { + TestAllTypes message; + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena_)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsBool()); + EXPECT_FALSE(result.BoolOrDie()); + } + { + TestAllTypes message; + message.mutable_single_nested_message()->set_bb(10); + Activation activation; + activation.InsertValue("message", + CelProtoWrapper::CreateMessage(&message, &arena_)); + ASSERT_OK_AND_ASSIGN(CelValue result, + cel_expr.Evaluate(activation, &arena_)); + ASSERT_TRUE(result.IsBool()); + EXPECT_TRUE(result.BoolOrDie()); + } +} + INSTANTIATE_TEST_SUITE_P(UnknownsEnabled, SelectStepConformanceTest, testing::Bool()); diff --git a/eval/public/BUILD b/eval/public/BUILD index d3340c893..47ee28bec 100644 --- a/eval/public/BUILD +++ b/eval/public/BUILD @@ -617,7 +617,7 @@ cc_library( "//extensions:select_optimization", "//internal:noop_delete", "//runtime:runtime_options", - "//runtime/internal:runtime_env", + "//runtime/internal:legacy_runtime_env", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/log:absl_log", diff --git a/eval/public/cel_expr_builder_factory.cc b/eval/public/cel_expr_builder_factory.cc index 14f2b4111..a677b8340 100644 --- a/eval/public/cel_expr_builder_factory.cc +++ b/eval/public/cel_expr_builder_factory.cc @@ -35,7 +35,7 @@ #include "eval/public/cel_options.h" #include "extensions/select_optimization.h" #include "internal/noop_delete.h" -#include "runtime/internal/runtime_env.h" +#include "runtime/internal/legacy_runtime_env.h" #include "runtime/runtime_options.h" #include "google/protobuf/arena.h" #include "google/protobuf/descriptor.h" @@ -51,7 +51,7 @@ using ::cel::extensions::kCelAttribute; using ::cel::extensions::kCelHasField; using ::cel::extensions::SelectOptimizationAstUpdater; using ::cel::runtime_internal::CreateConstantFoldingOptimizer; -using ::cel::runtime_internal::RuntimeEnv; +using ::cel::runtime_internal::LegacyRuntimeEnv; } // namespace @@ -72,18 +72,18 @@ std::unique_ptr CreateCelExpressionBuilder( message_factory, cel::internal::NoopDeleteFor()); } - auto env = std::make_shared( + auto env_or = LegacyRuntimeEnv::Create( std::shared_ptr( descriptor_pool, cel::internal::NoopDeleteFor()), shared_message_factory); - if (auto status = env->Initialize(); !status.ok()) { + if (!env_or.ok()) { ABSL_LOG(ERROR) << "Failed to validate standard message types: " - << status.ToString(); // NOLINT: OSS compatibility + << env_or.status().ToString(); // NOLINT: OSS compatibility return nullptr; } auto builder = std::make_unique( - std::move(env), runtime_options); + *std::move(env_or), runtime_options); FlatExprBuilder& flat_expr_builder = builder->flat_expr_builder(); diff --git a/eval/public/structs/BUILD b/eval/public/structs/BUILD index 1eb7d0772..afeb20038 100644 --- a/eval/public/structs/BUILD +++ b/eval/public/structs/BUILD @@ -31,6 +31,7 @@ cc_library( ":cel_proto_wrap_value_to_message", ":proto_message_type_adapter", ":trivial_legacy_type_info_internal", + "//common:legacy_value", "//common:value", "//eval/public:cel_value", "//eval/public:message_wrapper", @@ -128,6 +129,7 @@ cc_library( ], deps = [ "//eval/public:cel_value", + "//internal:json", "//internal:overflow", "//internal:proto_time_encoding", "//internal:status_macros", @@ -364,6 +366,7 @@ cc_test( ":proto_message_type_adapter", ":trivial_legacy_type_info", "//base:attributes", + "//common:legacy_value", "//common:value", "//common:value_testing", "//eval/public:cel_value", diff --git a/eval/public/structs/cel_proto_wrap_value_to_message.cc b/eval/public/structs/cel_proto_wrap_value_to_message.cc index 0b04e93d2..fb36bea92 100644 --- a/eval/public/structs/cel_proto_wrap_value_to_message.cc +++ b/eval/public/structs/cel_proto_wrap_value_to_message.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include "google/protobuf/any.pb.h" #include "google/protobuf/duration.pb.h" @@ -34,6 +35,7 @@ #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "eval/public/cel_value.h" +#include "internal/json.h" #include "internal/overflow.h" #include "internal/proto_time_encoding.h" #include "internal/status_macros.h" @@ -41,7 +43,6 @@ #include "internal/well_known_types.h" #include "google/protobuf/arena.h" #include "google/protobuf/descriptor.h" -#include "google/protobuf/json/json.h" #include "google/protobuf/message.h" namespace google::api::expr::runtime::internal { @@ -63,8 +64,6 @@ using google::protobuf::Arena; using google::protobuf::Descriptor; using google::protobuf::Message; using google::protobuf::MessageFactory; -using google::protobuf::json::MessageToJsonString; -using google::protobuf::json::PrintOptions; // kMaxIntJSON is defined as the Number.MAX_SAFE_INTEGER value per EcmaScript 6. constexpr int64_t kMaxIntJSON = (1ll << 53) - 1; @@ -94,23 +93,28 @@ static bool IsFieldMaskProto(const google::protobuf::Descriptor* descriptor) { static std::optional GetFieldMaskJsonString( const google::protobuf::Message& message) { - // TODO(b/540507668): Refactor to pipe descriptor_pool through - // ValueFromValue to use internal::MessageToJson. - PrintOptions json_options; - std::string json_str; - auto status = MessageToJsonString(message, &json_str, json_options); + Value json_value; + const google::protobuf::Descriptor* descriptor = message.GetDescriptor(); + const google::protobuf::Reflection* reflection = message.GetReflection(); + if (descriptor == nullptr || reflection == nullptr) { + ABSL_LOG(ERROR) << "Failed to convert FieldMask to JSON: missing " + "descriptor or reflection"; + return std::nullopt; + } + const google::protobuf::DescriptorPool* pool = descriptor->file()->pool(); + google::protobuf::MessageFactory* factory = reflection->GetMessageFactory(); + if (pool == nullptr || factory == nullptr) { + ABSL_LOG(ERROR) << "Failed to convert FieldMask to JSON: missing " + "descriptor pool or message factory"; + return std::nullopt; + } + absl::Status status = + cel::internal::MessageToJson(message, pool, factory, &json_value); if (!status.ok()) { ABSL_LOG(ERROR) << "Failed to convert FieldMask to JSON: " << status; return std::nullopt; } - // If JSON marshalling is correct, we know we'll always get a plain - // JSON string value and it shouldn't contain any escapes that we need - // to interpret. - if (json_str.size() >= 2 && json_str.front() == '"' && - json_str.back() == '"') { - return json_str.substr(1, json_str.size() - 2); - } - return json_str; + return std::move(*json_value.mutable_string_value()); } struct IgnoreErrorAndReturnNullptr { diff --git a/eval/public/structs/cel_proto_wrap_value_to_message_test.cc b/eval/public/structs/cel_proto_wrap_value_to_message_test.cc index 40b9398e2..26238231b 100644 --- a/eval/public/structs/cel_proto_wrap_value_to_message_test.cc +++ b/eval/public/structs/cel_proto_wrap_value_to_message_test.cc @@ -505,6 +505,38 @@ TEST_F(CelProtoWrapValueToMessageTest, WrapFieldMaskToValue) { ExpectWrappedMessage(value, expected_message); } +TEST_F(CelProtoWrapValueToMessageTest, WrapFieldMaskSnakeCaseToValue) { + FieldMask field_mask; + ASSERT_TRUE(TextFormat::ParseFromString( + R"pb(paths: "foo_bar.baz_qux" paths: "some_field")pb", &field_mask)); + CelValue value = MessageToCelValue(&field_mask); + + Value expected_message; + ASSERT_TRUE(TextFormat::ParseFromString( + R"pb(string_value: "fooBar.bazQux,someField")pb", &expected_message)); + + ExpectWrappedMessage(value, expected_message); +} + +TEST_F(CelProtoWrapValueToMessageTest, WrapEmptyFieldMaskToValue) { + FieldMask field_mask; + CelValue value = MessageToCelValue(&field_mask); + + Value expected_message; + expected_message.set_string_value(""); + + ExpectWrappedMessage(value, expected_message); +} + +TEST_F(CelProtoWrapValueToMessageTest, WrapInvalidFieldMaskToValue) { + FieldMask field_mask; + field_mask.add_paths("Foo"); + CelValue value = MessageToCelValue(&field_mask); + + Value expected_message; + ExpectNotWrapped(value, expected_message); +} + TEST_F(CelProtoWrapValueToMessageTest, WrapMapWithFieldMaskToAny) { const std::string kField = "field_mask"; FieldMask field_mask; diff --git a/extensions/BUILD b/extensions/BUILD index d9f8d7684..f4b2098aa 100644 --- a/extensions/BUILD +++ b/extensions/BUILD @@ -4,17 +4,15 @@ load("@rules_cc//cc:cc_test.bzl", "cc_test") package(default_visibility = ["//visibility:public"]) cc_library( - name = "encoders", - srcs = ["encoders.cc"], - hdrs = ["encoders.h"], + name = "encoders_modern", + srcs = ["encoders_modern.cc"], + hdrs = ["encoders_modern.h"], deps = [ "//checker:type_checker_builder", "//common:decl", "//common:type", "//common:value", "//compiler", - "//eval/public:cel_function_registry", - "//eval/public:cel_options", "//internal:status_macros", "//runtime:function_adapter", "//runtime:function_registry", @@ -27,6 +25,19 @@ cc_library( ], ) +cc_library( + name = "encoders", + srcs = ["encoders.cc"], + hdrs = ["encoders.h"], + deps = [ + ":encoders_modern", + "//eval/public:cel_function_registry", + "//eval/public:cel_options", + "@com_google_absl//absl/base:nullability", + "@com_google_absl//absl/status", + ], +) + cc_test( name = "encoders_test", srcs = ["encoders_test.cc"], @@ -71,15 +82,13 @@ cc_library( ) cc_library( - name = "math_ext", - srcs = ["math_ext.cc"], - hdrs = ["math_ext.h"], + name = "math_ext_modern", + srcs = ["math_ext_modern.cc"], + hdrs = ["math_ext_modern.h"], deps = [ ":math_ext_decls", "//common:value", - "//eval/public:cel_function_registry", - "//eval/public:cel_number", - "//eval/public:cel_options", + "//internal:number", "//internal:status_macros", "//runtime:function", "//runtime:function_adapter", @@ -95,6 +104,18 @@ cc_library( ], ) +cc_library( + name = "math_ext", + srcs = ["math_ext.cc"], + hdrs = ["math_ext.h"], + deps = [ + ":math_ext_modern", + "//eval/public:cel_function_registry", + "//eval/public:cel_options", + "@com_google_absl//absl/status", + ], +) + cc_library( name = "math_ext_macros", srcs = ["math_ext_macros.cc"], @@ -181,34 +202,47 @@ cc_test( # New users should use ":regex_ext" instead. cc_library( - name = "regex_functions", - srcs = ["regex_functions.cc"], - hdrs = ["regex_functions.h"], + name = "regex_functions_modern", + srcs = ["regex_functions_modern.cc"], + hdrs = ["regex_functions_modern.h"], deps = [ "//checker:type_checker_builder", "//checker/internal:builtins_arena", "//common:decl", "//common:type", "//common:value", - "//eval/public:cel_function_registry", - "//eval/public:cel_options", "//internal:re2_options", "//internal:status_macros", "//runtime:function_adapter", "//runtime:function_registry", "//runtime:runtime_options", - "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/base:no_destructor", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/functional:bind_front", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:string_view", "@com_google_protobuf//:protobuf", "@com_googlesource_code_re2//:re2", ], ) +# New users should use ":regex_ext" instead. +cc_library( + name = "regex_functions", + srcs = ["regex_functions.cc"], + hdrs = ["regex_functions.h"], + deps = [ + ":regex_functions_modern", + "//eval/public:cel_function_registry", + "//eval/public:cel_options", + "//internal:status_macros", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/status", + ], +) + cc_library( name = "bindings_ext", srcs = ["bindings_ext.cc"], @@ -329,7 +363,6 @@ cc_library( "//common:expr", "//common:function_descriptor", "//common:kind", - "//common:legacy_value", "//common:native_type", "//common:type", "//common:value", @@ -390,7 +423,6 @@ cc_test( "//eval/compiler:resolver", "//eval/eval:evaluator_core", "//eval/internal:interop", - "//eval/public:cel_type_registry", "//eval/public:cel_value", "//eval/public/structs:cel_proto_wrapper", "//extensions/protobuf:ast_converters", @@ -503,9 +535,9 @@ cc_test( ) cc_library( - name = "sets_functions", - srcs = ["sets_functions.cc"], - hdrs = ["sets_functions.h"], + name = "sets_functions_modern", + srcs = ["sets_functions_modern.cc"], + hdrs = ["sets_functions_modern.h"], deps = [ "//base:function_adapter", "//checker:type_checker_builder", @@ -513,8 +545,6 @@ cc_library( "//common:type", "//common:value", "//compiler", - "//eval/public:cel_function_registry", - "//eval/public:cel_options", "//internal:status_macros", "//runtime:function_registry", "//runtime:runtime_options", @@ -525,6 +555,18 @@ cc_library( ], ) +cc_library( + name = "sets_functions", + srcs = ["sets_functions.cc"], + hdrs = ["sets_functions.h"], + deps = [ + ":sets_functions_modern", + "//eval/public:cel_function_registry", + "//eval/public:cel_options", + "@com_google_absl//absl/status", + ], +) + cc_test( name = "sets_functions_test", srcs = ["sets_functions_test.cc"], @@ -568,6 +610,7 @@ cc_test( "//checker:validation_result", "//common:ast", "//common:decl", + "//common:legacy_value", "//common:minimal_descriptor_pool", "//common:type", "//common:value", @@ -596,9 +639,9 @@ cc_test( ) cc_library( - name = "strings", - srcs = ["strings.cc"], - hdrs = ["strings.h"], + name = "strings_modern", + srcs = ["strings_modern.cc"], + hdrs = ["strings_modern.h"], deps = [ ":formatting", "//checker:type_checker_builder", @@ -607,8 +650,6 @@ cc_library( "//common:type", "//common:value", "//compiler", - "//eval/public:cel_function_registry", - "//eval/public:cel_options", "//internal:status_macros", "//runtime:function", "//runtime:function_adapter", @@ -619,12 +660,23 @@ cc_library( "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:cord", "@com_google_absl//absl/strings:string_view", "@com_google_protobuf//:protobuf", ], ) +cc_library( + name = "strings", + srcs = ["strings.cc"], + hdrs = ["strings.h"], + deps = [ + ":strings_modern", + "//eval/public:cel_function_registry", + "//eval/public:cel_options", + "@com_google_absl//absl/status", + ], +) + cc_test( name = "strings_test", srcs = ["strings_test.cc"], @@ -661,13 +713,11 @@ cc_test( ) cc_library( - name = "comprehensions_v2_functions", - srcs = ["comprehensions_v2_functions.cc"], - hdrs = ["comprehensions_v2_functions.h"], + name = "comprehensions_v2_functions_modern", + srcs = ["comprehensions_v2_functions_modern.cc"], + hdrs = ["comprehensions_v2_functions_modern.h"], deps = [ "//common:value", - "//eval/public:cel_function_registry", - "//eval/public:cel_options", "//internal:status_macros", "//runtime:function_adapter", "//runtime:function_registry", @@ -679,6 +729,18 @@ cc_library( ], ) +cc_library( + name = "comprehensions_v2_functions", + srcs = ["comprehensions_v2_functions.cc"], + hdrs = ["comprehensions_v2_functions.h"], + deps = [ + ":comprehensions_v2_functions_modern", + "//eval/public:cel_function_registry", + "//eval/public:cel_options", + "@com_google_absl//absl/status", + ], +) + cc_library( name = "comprehensions_v2_macros", srcs = ["comprehensions_v2_macros.cc"], @@ -778,9 +840,9 @@ cc_library( ) cc_library( - name = "regex_ext", - srcs = ["regex_ext.cc"], - hdrs = ["regex_ext.h"], + name = "regex_ext_modern", + srcs = ["regex_ext_modern.cc"], + hdrs = ["regex_ext_modern.h"], deps = [ "//checker:type_checker_builder", "//checker/internal:builtins_arena", @@ -788,8 +850,6 @@ cc_library( "//common:type", "//common:value", "//compiler", - "//eval/public:cel_function_registry", - "//eval/public:cel_options", "//internal:casts", "//internal:re2_options", "//internal:status_macros", @@ -812,6 +872,18 @@ cc_library( ], ) +cc_library( + name = "regex_ext", + srcs = ["regex_ext.cc"], + hdrs = ["regex_ext.h"], + deps = [ + ":regex_ext_modern", + "//eval/public:cel_function_registry", + "//eval/public:cel_options", + "@com_google_absl//absl/status", + ], +) + cc_test( name = "regex_ext_test", srcs = ["regex_ext_test.cc"], diff --git a/extensions/comprehensions_v2_functions.cc b/extensions/comprehensions_v2_functions.cc index b6414b5d4..b2dc1356b 100644 --- a/extensions/comprehensions_v2_functions.cc +++ b/extensions/comprehensions_v2_functions.cc @@ -14,129 +14,12 @@ #include "extensions/comprehensions_v2_functions.h" -#include - -#include "absl/base/nullability.h" #include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "common/value.h" -#include "common/values/map_value_builder.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "internal/status_macros.h" -#include "runtime/function_adapter.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" -#include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" namespace cel::extensions { -namespace { - -absl::StatusOr MapInsertKeyValue( - const MapValue& map, const Value& key, const Value& value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - if (auto mutable_map_value = common_internal::AsMutableMapValue(map); - mutable_map_value) { - // Fast path, runtime has given us a mutable map. We can mutate it directly - // and return it. - CEL_RETURN_IF_ERROR(mutable_map_value->Put(key, value)) - .With(ErrorValueReturn(arena)); - return map; - } - // Slow path, we have to make a copy. - auto builder = NewMapValueBuilder(arena); - if (auto size = map.Size(); size.ok()) { - builder->Reserve(*size + 1); - } else { - size.IgnoreError(); - } - CEL_RETURN_IF_ERROR( - map.ForEach( - [&builder](const Value& key, - const Value& value) -> absl::StatusOr { - CEL_RETURN_IF_ERROR(builder->Put(key, value)); - return true; - }, - descriptor_pool, message_factory, arena)) - .With(ErrorValueReturn(arena)); - CEL_RETURN_IF_ERROR(builder->Put(key, value)).With(ErrorValueReturn(arena)); - return std::move(*builder).Build(); -} - -absl::StatusOr MapInsertMap( - const MapValue& map, const MapValue& value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - if (auto mutable_map_value = common_internal::AsMutableMapValue(map); - mutable_map_value) { - // Fast path, runtime has given us a mutable map. We can mutate it directly - // and return it. - CEL_RETURN_IF_ERROR( - value.ForEach( - [&mutable_map_value](const Value& key, - const Value& value) -> absl::StatusOr { - CEL_RETURN_IF_ERROR(mutable_map_value->Put(key, value)); - return true; - }, - descriptor_pool, message_factory, arena)) - .With(ErrorValueReturn(arena)); - return map; - } - // Slow path, we have to make a copy. - auto builder = NewMapValueBuilder(arena); - if (auto size = map.Size(); size.ok()) { - builder->Reserve(*size + 1); - } else { - size.IgnoreError(); - } - CEL_RETURN_IF_ERROR( - map.ForEach( - [&builder](const Value& key, - const Value& value) -> absl::StatusOr { - CEL_RETURN_IF_ERROR(builder->Put(key, value)); - return true; - }, - descriptor_pool, message_factory, arena)) - .With(ErrorValueReturn(arena)); - CEL_RETURN_IF_ERROR( - value.ForEach( - [&builder](const Value& key, - const Value& value) -> absl::StatusOr { - CEL_RETURN_IF_ERROR(builder->Put(key, value)); - return true; - }, - descriptor_pool, message_factory, arena)) - .With(ErrorValueReturn(arena)); - return std::move(*builder).Build(); -} - -} // namespace - -absl::Status RegisterComprehensionsV2Functions(FunctionRegistry& registry, - const RuntimeOptions& options) { - CEL_RETURN_IF_ERROR(registry.Register( - TernaryFunctionAdapter, MapValue, Value, - Value>::CreateDescriptor("cel.@mapInsert", - /*receiver_style=*/false), - TernaryFunctionAdapter, MapValue, Value, - Value>::WrapFunction(&MapInsertKeyValue))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter, MapValue, MapValue>:: - CreateDescriptor("cel.@mapInsert", - /*receiver_style=*/false), - BinaryFunctionAdapter, MapValue, - MapValue>::WrapFunction(&MapInsertMap))); - - return absl::OkStatus(); -} - absl::Status RegisterComprehensionsV2Functions( google::api::expr::runtime::CelFunctionRegistry* registry, const google::api::expr::runtime::InterpreterOptions& options) { diff --git a/extensions/comprehensions_v2_functions.h b/extensions/comprehensions_v2_functions.h index 8f99780a2..9eee9d937 100644 --- a/extensions/comprehensions_v2_functions.h +++ b/extensions/comprehensions_v2_functions.h @@ -18,14 +18,10 @@ #include "absl/status/status.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" +#include "extensions/comprehensions_v2_functions_modern.h" // IWYU pragma: export namespace cel::extensions { -// Register comprehension v2 functions. -absl::Status RegisterComprehensionsV2Functions(FunctionRegistry& registry, - const RuntimeOptions& options); absl::Status RegisterComprehensionsV2Functions( google::api::expr::runtime::CelFunctionRegistry* registry, const google::api::expr::runtime::InterpreterOptions& options); diff --git a/extensions/comprehensions_v2_functions_modern.cc b/extensions/comprehensions_v2_functions_modern.cc new file mode 100644 index 000000000..998f20bff --- /dev/null +++ b/extensions/comprehensions_v2_functions_modern.cc @@ -0,0 +1,140 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "extensions/comprehensions_v2_functions_modern.h" + +#include + +#include "absl/base/nullability.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "common/value.h" +#include "common/values/map_value_builder.h" +#include "internal/status_macros.h" +#include "runtime/function_adapter.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" + +namespace cel::extensions { + +namespace { + +absl::StatusOr MapInsertKeyValue( + const MapValue& map, const Value& key, const Value& value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + if (auto mutable_map_value = common_internal::AsMutableMapValue(map); + mutable_map_value) { + // Fast path, runtime has given us a mutable map. We can mutate it directly + // and return it. + CEL_RETURN_IF_ERROR(mutable_map_value->Put(key, value)) + .With(ErrorValueReturn(arena)); + return map; + } + // Slow path, we have to make a copy. + cel::MapValueBuilderPtr builder = NewMapValueBuilder(arena); + if (absl::StatusOr size = map.Size(); size.ok()) { + builder->Reserve(*size + 1); + } else { + size.IgnoreError(); + } + CEL_RETURN_IF_ERROR( + map.ForEach( + [&builder](const Value& entry_key, + const Value& entry_value) -> absl::StatusOr { + CEL_RETURN_IF_ERROR(builder->Put(entry_key, entry_value)); + return true; + }, + descriptor_pool, message_factory, arena)) + .With(ErrorValueReturn(arena)); + CEL_RETURN_IF_ERROR(builder->Put(key, value)).With(ErrorValueReturn(arena)); + return std::move(*builder).Build(); +} + +absl::StatusOr MapInsertMap( + const MapValue& map, const MapValue& value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + if (auto mutable_map_value = common_internal::AsMutableMapValue(map); + mutable_map_value) { + // Fast path, runtime has given us a mutable map. We can mutate it directly + // and return it. + CEL_RETURN_IF_ERROR( + value.ForEach( + [&mutable_map_value]( + const Value& entry_key, + const Value& entry_value) -> absl::StatusOr { + CEL_RETURN_IF_ERROR( + mutable_map_value->Put(entry_key, entry_value)); + return true; + }, + descriptor_pool, message_factory, arena)) + .With(ErrorValueReturn(arena)); + return map; + } + // Slow path, we have to make a copy. + cel::MapValueBuilderPtr builder = NewMapValueBuilder(arena); + if (absl::StatusOr size = map.Size(); size.ok()) { + builder->Reserve(*size + 1); + } else { + size.IgnoreError(); + } + CEL_RETURN_IF_ERROR( + map.ForEach( + [&builder](const Value& entry_key, + const Value& entry_value) -> absl::StatusOr { + CEL_RETURN_IF_ERROR(builder->Put(entry_key, entry_value)); + return true; + }, + descriptor_pool, message_factory, arena)) + .With(ErrorValueReturn(arena)); + CEL_RETURN_IF_ERROR( + value.ForEach( + [&builder](const Value& entry_key, + const Value& entry_value) -> absl::StatusOr { + CEL_RETURN_IF_ERROR(builder->Put(entry_key, entry_value)); + return true; + }, + descriptor_pool, message_factory, arena)) + .With(ErrorValueReturn(arena)); + return std::move(*builder).Build(); +} + +} // namespace + +absl::Status RegisterComprehensionsV2Functions(FunctionRegistry& registry, + const RuntimeOptions& options) { + CEL_RETURN_IF_ERROR(registry.Register( + TernaryFunctionAdapter, MapValue, Value, + Value>::CreateDescriptor("cel.@mapInsert", + /*receiver_style=*/false), + TernaryFunctionAdapter, MapValue, Value, + Value>::WrapFunction(&MapInsertKeyValue))); + + CEL_RETURN_IF_ERROR(registry.Register( + BinaryFunctionAdapter, MapValue, MapValue>:: + CreateDescriptor("cel.@mapInsert", + /*receiver_style=*/false), + BinaryFunctionAdapter, MapValue, + MapValue>::WrapFunction(&MapInsertMap))); + + return absl::OkStatus(); +} + +} // namespace cel::extensions diff --git a/extensions/comprehensions_v2_functions_modern.h b/extensions/comprehensions_v2_functions_modern.h new file mode 100644 index 000000000..3aec007ba --- /dev/null +++ b/extensions/comprehensions_v2_functions_modern.h @@ -0,0 +1,30 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_COMPREHENSIONS_V2_FUNCTIONS_MODERN_H_ +#define THIRD_PARTY_CEL_CPP_EXTENSIONS_COMPREHENSIONS_V2_FUNCTIONS_MODERN_H_ + +#include "absl/status/status.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" + +namespace cel::extensions { + +// Register comprehension v2 functions. +absl::Status RegisterComprehensionsV2Functions(FunctionRegistry& registry, + const RuntimeOptions& options); + +} // namespace cel::extensions + +#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_COMPREHENSIONS_V2_FUNCTIONS_MODERN_H_ diff --git a/extensions/encoders.cc b/extensions/encoders.cc index 4aeb00cca..e1a6737a0 100644 --- a/extensions/encoders.cc +++ b/extensions/encoders.cc @@ -14,93 +14,13 @@ #include "extensions/encoders.h" -#include -#include - #include "absl/base/nullability.h" #include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/escaping.h" -#include "checker/type_checker_builder.h" -#include "common/decl.h" -#include "common/type.h" -#include "common/value.h" -#include "compiler/compiler.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "internal/status_macros.h" -#include "runtime/function_adapter.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" -#include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" namespace cel::extensions { -namespace { - -absl::StatusOr Base64Decode( - const StringValue& value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - std::string in; - std::string out; - if (!absl::Base64Unescape(value.NativeString(in), &out)) { - return ErrorValue::From(absl::InvalidArgumentError("invalid base64 data"), - arena); - } - return BytesValue::From(std::move(out), arena); -} - -absl::StatusOr Base64Encode( - const BytesValue& value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - std::string in; - std::string out; - out = absl::Base64Escape(value.NativeString(in)); - return StringValue::From(std::move(out), arena); -} - -absl::Status RegisterEncodersDecls(TypeCheckerBuilder& builder) { - CEL_ASSIGN_OR_RETURN( - auto base64_decode_decl, - MakeFunctionDecl( - "base64.decode", - MakeOverloadDecl("base64_decode_string", BytesType(), StringType()))); - - CEL_ASSIGN_OR_RETURN( - auto base64_encode_decl, - MakeFunctionDecl( - "base64.encode", - MakeOverloadDecl("base64_encode_bytes", StringType(), BytesType()))); - - CEL_RETURN_IF_ERROR(builder.AddFunction(base64_decode_decl)); - CEL_RETURN_IF_ERROR(builder.AddFunction(base64_encode_decl)); - return absl::OkStatus(); -} - -} // namespace - -absl::Status RegisterEncodersFunctions(FunctionRegistry& registry, - const RuntimeOptions&) { - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter, - StringValue>::CreateDescriptor("base64.decode", - false), - UnaryFunctionAdapter, StringValue>::WrapFunction( - &Base64Decode))); - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter, BytesValue>::CreateDescriptor( - "base64.encode", false), - UnaryFunctionAdapter, BytesValue>::WrapFunction( - &Base64Encode))); - return absl::OkStatus(); -} - absl::Status RegisterEncodersFunctions( google::api::expr::runtime::CelFunctionRegistry* absl_nonnull registry, const google::api::expr::runtime::InterpreterOptions& options) { @@ -109,12 +29,4 @@ absl::Status RegisterEncodersFunctions( google::api::expr::runtime::ConvertToRuntimeOptions(options)); } -CheckerLibrary EncodersCheckerLibrary() { - return {"cel.lib.ext.encoders", &RegisterEncodersDecls}; -} - -CompilerLibrary EncodersCompilerLibrary() { - return CompilerLibrary::FromCheckerLibrary(EncodersCheckerLibrary()); -} - } // namespace cel::extensions diff --git a/extensions/encoders.h b/extensions/encoders.h index 2187f7fc6..018523ded 100644 --- a/extensions/encoders.h +++ b/extensions/encoders.h @@ -17,29 +17,16 @@ #include "absl/base/nullability.h" #include "absl/status/status.h" -#include "checker/type_checker_builder.h" -#include "compiler/compiler.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" +#include "extensions/encoders_modern.h" // IWYU pragma: export namespace cel::extensions { -// Register encoders functions. -absl::Status RegisterEncodersFunctions(FunctionRegistry& registry, - const RuntimeOptions& options); - absl::Status RegisterEncodersFunctions( google::api::expr::runtime::CelFunctionRegistry* absl_nonnull registry, const google::api::expr::runtime::InterpreterOptions& options); -// Declarations for the encoders extension library. -CheckerLibrary EncodersCheckerLibrary(); - -// Compiler library for the encoders extension. -CompilerLibrary EncodersCompilerLibrary(); - } // namespace cel::extensions #endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_ENCODERS_H_ diff --git a/extensions/encoders_modern.cc b/extensions/encoders_modern.cc new file mode 100644 index 000000000..d984238b2 --- /dev/null +++ b/extensions/encoders_modern.cc @@ -0,0 +1,109 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "extensions/encoders_modern.h" + +#include +#include + +#include "absl/base/nullability.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/escaping.h" +#include "checker/type_checker_builder.h" +#include "common/decl.h" +#include "common/type.h" +#include "common/value.h" +#include "compiler/compiler.h" +#include "internal/status_macros.h" +#include "runtime/function_adapter.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" + +namespace cel::extensions { + +namespace { + +absl::StatusOr Base64Decode( + const StringValue& value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + std::string in; + std::string out; + if (!absl::Base64Unescape(value.NativeString(in), &out)) { + return ErrorValue::From(absl::InvalidArgumentError("invalid base64 data"), + arena); + } + return BytesValue::From(std::move(out), arena); +} + +absl::StatusOr Base64Encode( + const BytesValue& value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + std::string in; + std::string out; + out = absl::Base64Escape(value.NativeString(in)); + return StringValue::From(std::move(out), arena); +} + +absl::Status RegisterEncodersDecls(TypeCheckerBuilder& builder) { + CEL_ASSIGN_OR_RETURN( + auto base64_decode_decl, + MakeFunctionDecl( + "base64.decode", + MakeOverloadDecl("base64_decode_string", BytesType(), StringType()))); + + CEL_ASSIGN_OR_RETURN( + auto base64_encode_decl, + MakeFunctionDecl( + "base64.encode", + MakeOverloadDecl("base64_encode_bytes", StringType(), BytesType()))); + + CEL_RETURN_IF_ERROR(builder.AddFunction(base64_decode_decl)); + CEL_RETURN_IF_ERROR(builder.AddFunction(base64_encode_decl)); + return absl::OkStatus(); +} + +} // namespace + +absl::Status RegisterEncodersFunctions(FunctionRegistry& registry, + const RuntimeOptions&) { + CEL_RETURN_IF_ERROR(registry.Register( + UnaryFunctionAdapter, StringValue>:: + CreateDescriptor("base64.decode", /*receiver_style=*/false), + UnaryFunctionAdapter, StringValue>::WrapFunction( + &Base64Decode))); + CEL_RETURN_IF_ERROR(registry.Register( + UnaryFunctionAdapter, BytesValue>::CreateDescriptor( + "base64.encode", /*receiver_style=*/false), + UnaryFunctionAdapter, BytesValue>::WrapFunction( + &Base64Encode))); + return absl::OkStatus(); +} + +CheckerLibrary EncodersCheckerLibrary() { + return {"cel.lib.ext.encoders", &RegisterEncodersDecls}; +} + +CompilerLibrary EncodersCompilerLibrary() { + return CompilerLibrary::FromCheckerLibrary(EncodersCheckerLibrary()); +} + +} // namespace cel::extensions diff --git a/extensions/encoders_modern.h b/extensions/encoders_modern.h new file mode 100644 index 000000000..616ab1896 --- /dev/null +++ b/extensions/encoders_modern.h @@ -0,0 +1,38 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_ENCODERS_MODERN_H_ +#define THIRD_PARTY_CEL_CPP_EXTENSIONS_ENCODERS_MODERN_H_ + +#include "absl/status/status.h" +#include "checker/type_checker_builder.h" +#include "compiler/compiler.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" + +namespace cel::extensions { + +// Register encoders functions. +absl::Status RegisterEncodersFunctions(FunctionRegistry& registry, + const RuntimeOptions& options); + +// Declarations for the encoders extension library. +CheckerLibrary EncodersCheckerLibrary(); + +// Compiler library for the encoders extension. +CompilerLibrary EncodersCompilerLibrary(); + +} // namespace cel::extensions + +#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_ENCODERS_MODERN_H_ diff --git a/extensions/math_ext.cc b/extensions/math_ext.cc index 49a0d2b38..49cda0a32 100644 --- a/extensions/math_ext.cc +++ b/extensions/math_ext.cc @@ -14,478 +14,15 @@ #include "extensions/math_ext.h" -#include -#include -#include - -#include "absl/base/casts.h" -#include "absl/base/nullability.h" -#include "absl/base/optimization.h" #include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "common/value.h" #include "eval/public/cel_function_registry.h" -#include "eval/public/cel_number.h" #include "eval/public/cel_options.h" -#include "internal/status_macros.h" -#include "runtime/function.h" -#include "runtime/function_adapter.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" -#include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" namespace cel::extensions { -namespace { - -using ::google::api::expr::runtime::CelFunctionRegistry; -using ::google::api::expr::runtime::CelNumber; -using ::google::api::expr::runtime::InterpreterOptions; - -static constexpr char kMathMin[] = "math.@min"; -static constexpr char kMathMax[] = "math.@max"; - -struct ToValueVisitor { - Value operator()(uint64_t v) const { return UintValue{v}; } - Value operator()(int64_t v) const { return IntValue{v}; } - Value operator()(double v) const { return DoubleValue{v}; } -}; - -Value NumberToValue(CelNumber number) { - return number.visit(ToValueVisitor{}); -} - -absl::StatusOr ValueToNumber(const Value& value, - absl::string_view function) { - if (auto int_value = value.AsInt(); int_value) { - return CelNumber::FromInt64(int_value->NativeValue()); - } - if (auto uint_value = value.AsUint(); uint_value) { - return CelNumber::FromUint64(uint_value->NativeValue()); - } - if (auto double_value = value.AsDouble(); double_value) { - return CelNumber::FromDouble(double_value->NativeValue()); - } - return absl::InvalidArgumentError( - absl::StrCat(function, " arguments must be numeric")); -} - -CelNumber MinNumber(CelNumber v1, CelNumber v2) { - if (v2 < v1) { - return v2; - } - return v1; -} - -Value MinValue(CelNumber v1, CelNumber v2) { - return NumberToValue(MinNumber(v1, v2)); -} - -template -Value Identity(T v1) { - return NumberToValue(CelNumber(v1)); -} - -template -Value Min(T v1, U v2) { - return MinValue(CelNumber(v1), CelNumber(v2)); -} - -absl::StatusOr MinList( - const ListValue& values, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - CEL_ASSIGN_OR_RETURN(auto iterator, values.NewIterator()); - if (!iterator->HasNext()) { - return ErrorValue::From( - absl::InvalidArgumentError("math.@min argument must not be empty"), - arena); - } - Value value; - CEL_RETURN_IF_ERROR( - iterator->Next(descriptor_pool, message_factory, arena, &value)); - absl::StatusOr current = ValueToNumber(value, kMathMin); - if (!current.ok()) { - return ErrorValue::From(current.status(), arena); - } - CelNumber min = *current; - while (iterator->HasNext()) { - CEL_RETURN_IF_ERROR( - iterator->Next(descriptor_pool, message_factory, arena, &value)); - absl::StatusOr other = ValueToNumber(value, kMathMin); - if (!other.ok()) { - return ErrorValue::From(other.status(), arena); - } - min = MinNumber(min, *other); - } - return NumberToValue(min); -} - -CelNumber MaxNumber(CelNumber v1, CelNumber v2) { - if (v2 > v1) { - return v2; - } - return v1; -} - -Value MaxValue(CelNumber v1, CelNumber v2) { - return NumberToValue(MaxNumber(v1, v2)); -} - -template -Value Max(T v1, U v2) { - return MaxValue(CelNumber(v1), CelNumber(v2)); -} - -absl::StatusOr MaxList( - const ListValue& values, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - CEL_ASSIGN_OR_RETURN(auto iterator, values.NewIterator()); - if (!iterator->HasNext()) { - return ErrorValue::From( - absl::InvalidArgumentError("math.@max argument must not be empty"), - arena); - } - Value value; - CEL_RETURN_IF_ERROR( - iterator->Next(descriptor_pool, message_factory, arena, &value)); - absl::StatusOr current = ValueToNumber(value, kMathMax); - if (!current.ok()) { - return ErrorValue::From(current.status(), arena); - } - CelNumber min = *current; - while (iterator->HasNext()) { - CEL_RETURN_IF_ERROR( - iterator->Next(descriptor_pool, message_factory, arena, &value)); - absl::StatusOr other = ValueToNumber(value, kMathMax); - if (!other.ok()) { - return ErrorValue::From(other.status(), arena); - } - min = MaxNumber(min, *other); - } - return NumberToValue(min); -} - -template -absl::Status RegisterCrossNumericMin(FunctionRegistry& registry) { - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMin, Min, registry))); - - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMin, Min, registry))); - - return absl::OkStatus(); -} - -template -absl::Status RegisterCrossNumericMax(FunctionRegistry& registry) { - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMax, Max, registry))); - - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMax, Max, registry))); - - return absl::OkStatus(); -} - -double CeilDouble(double value) { return std::ceil(value); } - -double FloorDouble(double value) { return std::floor(value); } - -double RoundDouble(double value) { return std::round(value); } - -double TruncDouble(double value) { return std::trunc(value); } - -double SqrtDouble(double value) { return std::sqrt(value); } - -double SqrtInt(int64_t value) { return std::sqrt(value); } - -double SqrtUint(uint64_t value) { return std::sqrt(value); } - -bool IsInfDouble(double value) { return std::isinf(value); } - -bool IsNaNDouble(double value) { return std::isnan(value); } - -bool IsFiniteDouble(double value) { return std::isfinite(value); } - -double AbsDouble(double value) { return std::fabs(value); } - -Value AbsInt(int64_t value, const Function::InvokeContext& context) { - if (ABSL_PREDICT_FALSE(value == std::numeric_limits::min())) { - return ErrorValue::From(absl::InvalidArgumentError("integer overflow"), - context.arena()); - } - return IntValue(value < 0 ? -value : value); -} - -uint64_t AbsUint(uint64_t value) { return value; } - -double SignDouble(double value) { - if (std::isnan(value)) { - return value; - } - if (value == 0.0) { - return 0.0; - } - return std::signbit(value) ? -1.0 : 1.0; -} - -int64_t SignInt(int64_t value) { return value < 0 ? -1 : value > 0 ? 1 : 0; } - -uint64_t SignUint(uint64_t value) { return value == 0 ? 0 : 1; } - -int64_t BitAndInt(int64_t lhs, int64_t rhs) { return lhs & rhs; } - -uint64_t BitAndUint(uint64_t lhs, uint64_t rhs) { return lhs & rhs; } - -int64_t BitOrInt(int64_t lhs, int64_t rhs) { return lhs | rhs; } - -uint64_t BitOrUint(uint64_t lhs, uint64_t rhs) { return lhs | rhs; } - -int64_t BitXorInt(int64_t lhs, int64_t rhs) { return lhs ^ rhs; } - -uint64_t BitXorUint(uint64_t lhs, uint64_t rhs) { return lhs ^ rhs; } - -int64_t BitNotInt(int64_t value) { return ~value; } - -uint64_t BitNotUint(uint64_t value) { return ~value; } - -Value BitShiftLeftInt(int64_t lhs, int64_t rhs, - const Function::InvokeContext& context) { - if (ABSL_PREDICT_FALSE(rhs < 0)) { - return ErrorValue::From( - absl::InvalidArgumentError( - absl::StrCat("math.bitShiftLeft() invalid negative shift: ", rhs)), - context.arena()); - } - if (rhs > 63) { - return IntValue(0); - } - // Shift in the unsigned domain to avoid undefined behaviour when lhs is - // negative or the shift moves bits into the sign bit, matching the bit - // pattern semantics already used by bitShiftRight. - return IntValue(absl::bit_cast(absl::bit_cast(lhs) - << static_cast(rhs))); -} - -Value BitShiftLeftUint(uint64_t lhs, int64_t rhs, - const Function::InvokeContext& context) { - if (ABSL_PREDICT_FALSE(rhs < 0)) { - return ErrorValue::From( - absl::InvalidArgumentError( - absl::StrCat("math.bitShiftLeft() invalid negative shift: ", rhs)), - context.arena()); - } - if (rhs > 63) { - return UintValue(0); - } - return UintValue(lhs << static_cast(rhs)); -} - -Value BitShiftRightInt(int64_t lhs, int64_t rhs, - const Function::InvokeContext& context) { - if (ABSL_PREDICT_FALSE(rhs < 0)) { - return ErrorValue::From( - absl::InvalidArgumentError( - absl::StrCat("math.bitShiftRight() invalid negative shift: ", rhs)), - context.arena()); - } - if (rhs > 63) { - return IntValue(0); - } - // We do not perform a sign extension shift, per the spec we just do the same - // thing as uint. - return IntValue(absl::bit_cast(absl::bit_cast(lhs) >> - static_cast(rhs))); -} - -Value BitShiftRightUint(uint64_t lhs, int64_t rhs, - const Function::InvokeContext& context) { - if (ABSL_PREDICT_FALSE(rhs < 0)) { - return ErrorValue::From( - absl::InvalidArgumentError( - absl::StrCat("math.bitShiftRight() invalid negative shift: ", rhs)), - context.arena()); - } - if (rhs > 63) { - return UintValue(0); - } - return UintValue(lhs >> static_cast(rhs)); -} - -} // namespace - -absl::Status RegisterMathExtensionFunctions(FunctionRegistry& registry, - const RuntimeOptions& options, - int version) { - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - kMathMin, Identity, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - kMathMin, Identity, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - kMathMin, Identity, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMin, Min, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMin, Min, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMin, Min, registry))); - CEL_RETURN_IF_ERROR((RegisterCrossNumericMin(registry))); - CEL_RETURN_IF_ERROR((RegisterCrossNumericMin(registry))); - CEL_RETURN_IF_ERROR((RegisterCrossNumericMin(registry))); - CEL_RETURN_IF_ERROR(( - UnaryFunctionAdapter, - ListValue>::RegisterGlobalOverload(kMathMin, MinList, - registry))); - - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - kMathMax, Identity, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - kMathMax, Identity, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - kMathMax, Identity, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMax, Max, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMax, Max, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - kMathMax, Max, registry))); - CEL_RETURN_IF_ERROR((RegisterCrossNumericMax(registry))); - CEL_RETURN_IF_ERROR((RegisterCrossNumericMax(registry))); - CEL_RETURN_IF_ERROR((RegisterCrossNumericMax(registry))); - CEL_RETURN_IF_ERROR(( - UnaryFunctionAdapter, - ListValue>::RegisterGlobalOverload(kMathMax, MaxList, - registry))); - if (version == 0) { - return absl::OkStatus(); - } - - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.ceil", CeilDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.floor", FloorDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.round", RoundDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.trunc", TruncDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.isInf", IsInfDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.isNaN", IsNaNDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.isFinite", IsFiniteDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.abs", AbsDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.abs", AbsInt, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.abs", AbsUint, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.sign", SignDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.sign", SignInt, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.sign", SignUint, registry))); - - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - "math.bitAnd", BitAndInt, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload("math.bitAnd", - BitAndUint, - registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - "math.bitOr", BitOrInt, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload("math.bitOr", - BitOrUint, - registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - "math.bitXor", BitXorInt, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload("math.bitXor", - BitXorUint, - registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.bitNot", BitNotInt, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.bitNot", BitNotUint, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - "math.bitShiftLeft", BitShiftLeftInt, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - "math.bitShiftLeft", BitShiftLeftUint, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - "math.bitShiftRight", BitShiftRightInt, registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterGlobalOverload( - "math.bitShiftRight", BitShiftRightUint, registry))); - - if (version == 1) { - return absl::OkStatus(); - } - - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.sqrt", SqrtDouble, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.sqrt", SqrtInt, registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "math.sqrt", SqrtUint, registry))); - - return absl::OkStatus(); -} - -absl::Status RegisterMathExtensionFunctions(CelFunctionRegistry* registry, - const InterpreterOptions& options) { +absl::Status RegisterMathExtensionFunctions( + google::api::expr::runtime::CelFunctionRegistry* registry, + const google::api::expr::runtime::InterpreterOptions& options) { return RegisterMathExtensionFunctions( registry->InternalGetRegistry(), google::api::expr::runtime::ConvertToRuntimeOptions(options)); diff --git a/extensions/math_ext.h b/extensions/math_ext.h index fe000e476..0bfe8f301 100644 --- a/extensions/math_ext.h +++ b/extensions/math_ext.h @@ -18,18 +18,10 @@ #include "absl/status/status.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "extensions/math_ext_decls.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" +#include "extensions/math_ext_modern.h" // IWYU pragma: export namespace cel::extensions { -// Register extension functions for supporting mathematical operations above -// and beyond the set defined in the CEL standard environment. -absl::Status RegisterMathExtensionFunctions( - FunctionRegistry& registry, const RuntimeOptions& options, - int version = kMathExtensionLatestVersion); - absl::Status RegisterMathExtensionFunctions( google::api::expr::runtime::CelFunctionRegistry* registry, const google::api::expr::runtime::InterpreterOptions& options); diff --git a/extensions/math_ext_modern.cc b/extensions/math_ext_modern.cc new file mode 100644 index 000000000..8ff94fd63 --- /dev/null +++ b/extensions/math_ext_modern.cc @@ -0,0 +1,483 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "extensions/math_ext_modern.h" + +#include +#include +#include + +#include "absl/base/casts.h" +#include "absl/base/nullability.h" +#include "absl/base/optimization.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "common/value.h" +#include "internal/number.h" +#include "internal/status_macros.h" +#include "runtime/function.h" +#include "runtime/function_adapter.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" + +namespace cel::extensions { + +namespace { + +using ::cel::internal::Number; + +constexpr absl::string_view kMathMin = "math.@min"; +constexpr absl::string_view kMathMax = "math.@max"; + +struct ToValueVisitor { + Value operator()(uint64_t v) const { return UintValue{v}; } + Value operator()(int64_t v) const { return IntValue{v}; } + Value operator()(double v) const { return DoubleValue{v}; } +}; + +Value NumberToValue(Number number) { + return number.visit(ToValueVisitor{}); +} + +absl::StatusOr ValueToNumber(const Value& value, + absl::string_view function) { + if (auto int_value = value.AsInt(); int_value) { + return Number::FromInt64(int_value->NativeValue()); + } + if (auto uint_value = value.AsUint(); uint_value) { + return Number::FromUint64(uint_value->NativeValue()); + } + if (auto double_value = value.AsDouble(); double_value) { + return Number::FromDouble(double_value->NativeValue()); + } + return absl::InvalidArgumentError( + absl::StrCat(function, " arguments must be numeric")); +} + +Number MinNumber(Number v1, Number v2) { + if (v2 < v1) { + return v2; + } + return v1; +} + +Value MinValue(Number v1, Number v2) { + return NumberToValue(MinNumber(v1, v2)); +} + +template +Value Identity(T v1) { + return NumberToValue(Number(v1)); +} + +template +Value Min(T v1, U v2) { + return MinValue(Number(v1), Number(v2)); +} + +absl::StatusOr MinList( + const ListValue& values, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + CEL_ASSIGN_OR_RETURN(auto iterator, values.NewIterator()); + if (!iterator->HasNext()) { + return ErrorValue::From( + absl::InvalidArgumentError("math.@min argument must not be empty"), + arena); + } + Value value; + CEL_RETURN_IF_ERROR( + iterator->Next(descriptor_pool, message_factory, arena, &value)); + absl::StatusOr current = ValueToNumber(value, kMathMin); + if (!current.ok()) { + return ErrorValue::From(current.status(), arena); + } + Number min = *current; + while (iterator->HasNext()) { + CEL_RETURN_IF_ERROR( + iterator->Next(descriptor_pool, message_factory, arena, &value)); + absl::StatusOr other = ValueToNumber(value, kMathMin); + if (!other.ok()) { + return ErrorValue::From(other.status(), arena); + } + min = MinNumber(min, *other); + } + return NumberToValue(min); +} + +Number MaxNumber(Number v1, Number v2) { + if (v2 > v1) { + return v2; + } + return v1; +} + +Value MaxValue(Number v1, Number v2) { + return NumberToValue(MaxNumber(v1, v2)); +} + +template +Value Max(T v1, U v2) { + return MaxValue(Number(v1), Number(v2)); +} + +absl::StatusOr MaxList( + const ListValue& values, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + CEL_ASSIGN_OR_RETURN(auto iterator, values.NewIterator()); + if (!iterator->HasNext()) { + return ErrorValue::From( + absl::InvalidArgumentError("math.@max argument must not be empty"), + arena); + } + Value value; + CEL_RETURN_IF_ERROR( + iterator->Next(descriptor_pool, message_factory, arena, &value)); + absl::StatusOr current = ValueToNumber(value, kMathMax); + if (!current.ok()) { + return ErrorValue::From(current.status(), arena); + } + Number max = *current; + while (iterator->HasNext()) { + CEL_RETURN_IF_ERROR( + iterator->Next(descriptor_pool, message_factory, arena, &value)); + absl::StatusOr other = ValueToNumber(value, kMathMax); + if (!other.ok()) { + return ErrorValue::From(other.status(), arena); + } + max = MaxNumber(max, *other); + } + return NumberToValue(max); +} + +template +absl::Status RegisterCrossNumericMin(FunctionRegistry& registry) { + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMin, Min, registry))); + + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMin, Min, registry))); + + return absl::OkStatus(); +} + +template +absl::Status RegisterCrossNumericMax(FunctionRegistry& registry) { + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMax, Max, registry))); + + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMax, Max, registry))); + + return absl::OkStatus(); +} + +double CeilDouble(double value) { return std::ceil(value); } + +double FloorDouble(double value) { return std::floor(value); } + +double RoundDouble(double value) { return std::round(value); } + +double TruncDouble(double value) { return std::trunc(value); } + +double SqrtDouble(double value) { return std::sqrt(value); } + +double SqrtInt(int64_t value) { return std::sqrt(value); } + +double SqrtUint(uint64_t value) { return std::sqrt(value); } + +bool IsInfDouble(double value) { return std::isinf(value); } + +bool IsNaNDouble(double value) { return std::isnan(value); } + +bool IsFiniteDouble(double value) { return std::isfinite(value); } + +double AbsDouble(double value) { return std::fabs(value); } + +Value AbsInt(int64_t value, const Function::InvokeContext& context) { + if (ABSL_PREDICT_FALSE(value == std::numeric_limits::min())) { + return ErrorValue::From(absl::InvalidArgumentError("integer overflow"), + context.arena()); + } + return IntValue(value < 0 ? -value : value); +} + +uint64_t AbsUint(uint64_t value) { return value; } + +double SignDouble(double value) { + if (std::isnan(value)) { + return value; + } + if (value == 0.0) { + return 0.0; + } + return std::signbit(value) ? -1.0 : 1.0; +} + +int64_t SignInt(int64_t value) { return value < 0 ? -1 : value > 0 ? 1 : 0; } + +uint64_t SignUint(uint64_t value) { return value == 0 ? 0 : 1; } + +int64_t BitAndInt(int64_t lhs, int64_t rhs) { return lhs & rhs; } + +uint64_t BitAndUint(uint64_t lhs, uint64_t rhs) { return lhs & rhs; } + +int64_t BitOrInt(int64_t lhs, int64_t rhs) { return lhs | rhs; } + +uint64_t BitOrUint(uint64_t lhs, uint64_t rhs) { return lhs | rhs; } + +int64_t BitXorInt(int64_t lhs, int64_t rhs) { return lhs ^ rhs; } + +uint64_t BitXorUint(uint64_t lhs, uint64_t rhs) { return lhs ^ rhs; } + +int64_t BitNotInt(int64_t value) { return ~value; } + +uint64_t BitNotUint(uint64_t value) { return ~value; } + +Value BitShiftLeftInt(int64_t lhs, int64_t rhs, + const Function::InvokeContext& context) { + if (ABSL_PREDICT_FALSE(rhs < 0)) { + return ErrorValue::From( + absl::InvalidArgumentError( + absl::StrCat("math.bitShiftLeft() invalid negative shift: ", rhs)), + context.arena()); + } + if (rhs > 63) { + return IntValue(0); + } + // Shift in the unsigned domain to avoid undefined behaviour when lhs is + // negative or the shift moves bits into the sign bit, matching the bit + // pattern semantics already used by bitShiftRight. + return IntValue(absl::bit_cast(absl::bit_cast(lhs) + << static_cast(rhs))); +} + +Value BitShiftLeftUint(uint64_t lhs, int64_t rhs, + const Function::InvokeContext& context) { + if (ABSL_PREDICT_FALSE(rhs < 0)) { + return ErrorValue::From( + absl::InvalidArgumentError( + absl::StrCat("math.bitShiftLeft() invalid negative shift: ", rhs)), + context.arena()); + } + if (rhs > 63) { + return UintValue(0); + } + return UintValue(lhs << static_cast(rhs)); +} + +Value BitShiftRightInt(int64_t lhs, int64_t rhs, + const Function::InvokeContext& context) { + if (ABSL_PREDICT_FALSE(rhs < 0)) { + return ErrorValue::From( + absl::InvalidArgumentError( + absl::StrCat("math.bitShiftRight() invalid negative shift: ", rhs)), + context.arena()); + } + if (rhs > 63) { + return IntValue(0); + } + // We do not perform a sign extension shift, per the spec we just do the same + // thing as uint. + return IntValue(absl::bit_cast(absl::bit_cast(lhs) >> + static_cast(rhs))); +} + +Value BitShiftRightUint(uint64_t lhs, int64_t rhs, + const Function::InvokeContext& context) { + if (ABSL_PREDICT_FALSE(rhs < 0)) { + return ErrorValue::From( + absl::InvalidArgumentError( + absl::StrCat("math.bitShiftRight() invalid negative shift: ", rhs)), + context.arena()); + } + if (rhs > 63) { + return UintValue(0); + } + return UintValue(lhs >> static_cast(rhs)); +} + +} // namespace + +absl::Status RegisterMathExtensionFunctions(FunctionRegistry& registry, + const RuntimeOptions& options, + int version) { + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + kMathMin, Identity, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + kMathMin, Identity, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + kMathMin, Identity, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMin, Min, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMin, Min, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMin, Min, registry))); + CEL_RETURN_IF_ERROR((RegisterCrossNumericMin(registry))); + CEL_RETURN_IF_ERROR((RegisterCrossNumericMin(registry))); + CEL_RETURN_IF_ERROR((RegisterCrossNumericMin(registry))); + CEL_RETURN_IF_ERROR(( + UnaryFunctionAdapter, + ListValue>::RegisterGlobalOverload(kMathMin, MinList, + registry))); + + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + kMathMax, Identity, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + kMathMax, Identity, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + kMathMax, Identity, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMax, Max, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMax, Max, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + kMathMax, Max, registry))); + CEL_RETURN_IF_ERROR((RegisterCrossNumericMax(registry))); + CEL_RETURN_IF_ERROR((RegisterCrossNumericMax(registry))); + CEL_RETURN_IF_ERROR((RegisterCrossNumericMax(registry))); + CEL_RETURN_IF_ERROR(( + UnaryFunctionAdapter, + ListValue>::RegisterGlobalOverload(kMathMax, MaxList, + registry))); + if (version == 0) { + return absl::OkStatus(); + } + + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.ceil", CeilDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.floor", FloorDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.round", RoundDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.trunc", TruncDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.isInf", IsInfDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.isNaN", IsNaNDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.isFinite", IsFiniteDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.abs", AbsDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.abs", AbsInt, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.abs", AbsUint, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.sign", SignDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.sign", SignInt, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.sign", SignUint, registry))); + + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + "math.bitAnd", BitAndInt, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload("math.bitAnd", + BitAndUint, + registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + "math.bitOr", BitOrInt, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload("math.bitOr", + BitOrUint, + registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + "math.bitXor", BitXorInt, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload("math.bitXor", + BitXorUint, + registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.bitNot", BitNotInt, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.bitNot", BitNotUint, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + "math.bitShiftLeft", BitShiftLeftInt, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + "math.bitShiftLeft", BitShiftLeftUint, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + "math.bitShiftRight", BitShiftRightInt, registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterGlobalOverload( + "math.bitShiftRight", BitShiftRightUint, registry))); + + if (version == 1) { + return absl::OkStatus(); + } + + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.sqrt", SqrtDouble, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.sqrt", SqrtInt, registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "math.sqrt", SqrtUint, registry))); + + return absl::OkStatus(); +} + +} // namespace cel::extensions diff --git a/extensions/math_ext_modern.h b/extensions/math_ext_modern.h new file mode 100644 index 000000000..5bc24781c --- /dev/null +++ b/extensions/math_ext_modern.h @@ -0,0 +1,33 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_MATH_EXT_MODERN_H_ +#define THIRD_PARTY_CEL_CPP_EXTENSIONS_MATH_EXT_MODERN_H_ + +#include "absl/status/status.h" +#include "extensions/math_ext_decls.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" + +namespace cel::extensions { + +// Register extension functions for supporting mathematical operations above +// and beyond the set defined in the CEL standard environment. +absl::Status RegisterMathExtensionFunctions( + FunctionRegistry& registry, const RuntimeOptions& options, + int version = kMathExtensionLatestVersion); + +} // namespace cel::extensions + +#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_MATH_EXT_MODERN_H_ diff --git a/extensions/regex_ext.cc b/extensions/regex_ext.cc index 065adc76d..311ffa22d 100644 --- a/extensions/regex_ext.cc +++ b/extensions/regex_ext.cc @@ -14,348 +14,21 @@ #include "extensions/regex_ext.h" -#include -#include -#include -#include - -#include "absl/base/no_destructor.h" -#include "absl/base/nullability.h" -#include "absl/functional/bind_front.h" #include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "checker/internal/builtins_arena.h" -#include "checker/type_checker_builder.h" -#include "common/decl.h" -#include "common/type.h" -#include "common/value.h" -#include "compiler/compiler.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "internal/casts.h" -#include "internal/re2_options.h" -#include "internal/status_macros.h" -#include "runtime/function_adapter.h" -#include "runtime/function_registry.h" -#include "runtime/internal/runtime_friend_access.h" -#include "runtime/internal/runtime_impl.h" -#include "runtime/runtime_builder.h" -#include "validator/regex_validator.h" -#include "validator/validator.h" -#include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" -#include "re2/re2.h" namespace cel::extensions { -namespace { - -using ::cel::checker_internal::BuiltinsArena; - -Value Extract(int regex_max_program_size, const StringValue& target, - const StringValue& regex, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - std::string target_scratch; - std::string regex_scratch; - absl::string_view target_view = target.ToStringView(&target_scratch); - absl::string_view regex_view = regex.ToStringView(®ex_scratch); - RE2 re2(regex_view, cel::internal::MakeRE2Options()); - CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) - .With(ErrorValueReturn(arena)); - const int group_count = re2.NumberOfCapturingGroups(); - if (group_count > 1) { - return ErrorValue::From( - absl::InvalidArgumentError(absl::StrFormat( - "regular expression has more than one capturing group: %s", - regex_view)), - arena); - } - - // Space for the full match (\0) and the first capture group (\1). - absl::string_view submatches[2]; - if (re2.Match(target_view, 0, target_view.length(), RE2::UNANCHORED, - submatches, 2)) { - // Return the capture group if it exists else return the full match. - const absl::string_view result_view = - (group_count == 1) ? submatches[1] : submatches[0]; - return OptionalValue::Of(StringValue::From(result_view, arena), arena); - } - - return OptionalValue::None(); -} - -Value ExtractAll(int regex_max_program_size, const StringValue& target, - const StringValue& regex, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - std::string target_scratch; - std::string regex_scratch; - absl::string_view target_view = target.ToStringView(&target_scratch); - absl::string_view regex_view = regex.ToStringView(®ex_scratch); - RE2 re2(regex_view, cel::internal::MakeRE2Options()); - CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) - .With(ErrorValueReturn(arena)); - const int group_count = re2.NumberOfCapturingGroups(); - if (group_count > 1) { - return ErrorValue::From( - absl::InvalidArgumentError(absl::StrFormat( - "regular expression has more than one capturing group: %s", - regex_view)), - arena); - } - - auto builder = NewListValueBuilder(arena); - absl::string_view temp_target = target_view; - - // Space for the full match (\0) and the first capture group (\1). - absl::string_view submatches[2]; - const int group_to_extract = (group_count == 1) ? 1 : 0; - - while (re2.Match(temp_target, 0, temp_target.length(), RE2::UNANCHORED, - submatches, group_count + 1)) { - const absl::string_view& full_match = submatches[0]; - const absl::string_view& desired_capture = submatches[group_to_extract]; - - // Avoid infinite loops on zero-length matches - if (full_match.empty()) { - if (temp_target.empty()) { - break; - } - temp_target.remove_prefix(1); - continue; - } - - if (group_count == 1 && desired_capture.empty()) { - temp_target.remove_prefix(full_match.data() - temp_target.data() + - full_match.length()); - continue; - } - - absl::Status status = - builder->Add(StringValue::From(desired_capture, arena)); - if (!status.ok()) { - return ErrorValue::From(status, arena); - } - temp_target.remove_prefix(full_match.data() - temp_target.data() + - full_match.length()); - } - - return std::move(*builder).Build(); -} - -Value ReplaceAll(int regex_max_program_size, const StringValue& target, - const StringValue& regex, const StringValue& replacement, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - std::string target_scratch; - std::string regex_scratch; - std::string replacement_scratch; - absl::string_view target_view = target.ToStringView(&target_scratch); - absl::string_view regex_view = regex.ToStringView(®ex_scratch); - absl::string_view replacement_view = - replacement.ToStringView(&replacement_scratch); - RE2 re2(regex_view, cel::internal::MakeRE2Options()); - CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) - .With(ErrorValueReturn(arena)); - std::string error_string; - if (!re2.CheckRewriteString(replacement_view, &error_string)) { - return ErrorValue::From( - absl::InvalidArgumentError( - absl::StrFormat("invalid replacement string: %s", error_string)), - arena); - } - - std::string output(target_view); - RE2::GlobalReplace(&output, re2, replacement_view); - - return StringValue::From(std::move(output), arena); -} - -Value ReplaceN(int regex_max_program_size, const StringValue& target, - const StringValue& regex, const StringValue& replacement, - int64_t count, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - if (count == 0) { - return target; - } - if (count < 0) { - return ReplaceAll(regex_max_program_size, target, regex, replacement, - descriptor_pool, message_factory, arena); - } - - std::string target_scratch; - std::string regex_scratch; - std::string replacement_scratch; - absl::string_view target_view = target.ToStringView(&target_scratch); - absl::string_view regex_view = regex.ToStringView(®ex_scratch); - absl::string_view replacement_view = - replacement.ToStringView(&replacement_scratch); - RE2 re2(regex_view, cel::internal::MakeRE2Options()); - CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) - .With(ErrorValueReturn(arena)); - std::string error_string; - if (!re2.CheckRewriteString(replacement_view, &error_string)) { - return ErrorValue::From( - absl::InvalidArgumentError( - absl::StrFormat("invalid replacement string: %s", error_string)), - arena); - } - - std::string output; - absl::string_view temp_target = target_view; - int replaced_count = 0; - // RE2's Rewrite only supports substitutions for groups \0 through \9. - absl::string_view match[10]; - int nmatch = std::min(9, re2.NumberOfCapturingGroups()) + 1; - - while (replaced_count < count && - re2.Match(temp_target, 0, temp_target.length(), RE2::UNANCHORED, match, - nmatch)) { - absl::string_view full_match = match[0]; - - output.append(temp_target.data(), full_match.data() - temp_target.data()); - - if (!re2.Rewrite(&output, replacement_view, match, nmatch)) { - // This should ideally not happen given CheckRewriteString passed - return ErrorValue::From( - absl::InternalError("rewrite failed unexpectedly"), arena); - } - - temp_target.remove_prefix(full_match.data() - temp_target.data() + - full_match.length()); - replaced_count++; - } - - output.append(temp_target.data(), temp_target.length()); - - return StringValue::From(std::move(output), arena); -} - -absl::Status RegisterRegexExtensionFunctions(FunctionRegistry& registry, - bool disable_extract, - int regex_max_program_size) { - if (!disable_extract) { - CEL_RETURN_IF_ERROR(( - BinaryFunctionAdapter, StringValue, StringValue>:: - RegisterGlobalOverload( - "regex.extract", - absl::bind_front(&Extract, regex_max_program_size), registry))); - } - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter, StringValue, StringValue>:: - RegisterGlobalOverload( - "regex.extractAll", - absl::bind_front(&ExtractAll, regex_max_program_size), - registry))); - CEL_RETURN_IF_ERROR( - (TernaryFunctionAdapter< - absl::StatusOr, StringValue, StringValue, - StringValue>::RegisterGlobalOverload("regex.replace", - absl::bind_front( - &ReplaceAll, - regex_max_program_size), - registry))); - CEL_RETURN_IF_ERROR( - (QuaternaryFunctionAdapter, StringValue, - StringValue, StringValue, int64_t>:: - RegisterGlobalOverload( - "regex.replace", - absl::bind_front(&ReplaceN, regex_max_program_size), registry))); - return absl::OkStatus(); -} - -const Type& OptionalStringType() { - static absl::NoDestructor kInstance( - OptionalType(BuiltinsArena(), StringType())); - return *kInstance; -} - -const Type& ListStringType() { - static absl::NoDestructor kInstance( - ListType(BuiltinsArena(), StringType())); - return *kInstance; -} - -absl::Status RegisterRegexCheckerDecls(TypeCheckerBuilder& builder) { - CEL_ASSIGN_OR_RETURN( - FunctionDecl extract_decl, - MakeFunctionDecl( - "regex.extract", - MakeOverloadDecl("regex_extract_string_string", OptionalStringType(), - StringType(), StringType()))); - - CEL_ASSIGN_OR_RETURN( - FunctionDecl extract_all_decl, - MakeFunctionDecl( - "regex.extractAll", - MakeOverloadDecl("regex_extractAll_string_string", ListStringType(), - StringType(), StringType()))); - - CEL_ASSIGN_OR_RETURN( - FunctionDecl replace_decl, - MakeFunctionDecl( - "regex.replace", - MakeOverloadDecl("regex_replace_string_string_string", StringType(), - StringType(), StringType(), StringType()), - MakeOverloadDecl("regex_replace_string_string_string_int", - StringType(), StringType(), StringType(), - StringType(), IntType()))); - - CEL_RETURN_IF_ERROR(builder.AddFunction(extract_decl)); - CEL_RETURN_IF_ERROR(builder.AddFunction(extract_all_decl)); - CEL_RETURN_IF_ERROR(builder.AddFunction(replace_decl)); - return absl::OkStatus(); -} - -} // namespace - -absl::Status RegisterRegexExtensionFunctions(RuntimeBuilder& builder) { - auto& runtime = cel::internal::down_cast( - runtime_internal::RuntimeFriendAccess::GetMutableRuntime(builder)); - if (!runtime.expr_builder().optional_types_enabled()) { - return absl::InvalidArgumentError( - "regex extensions requires the optional types to be enabled"); - } - if (runtime.expr_builder().options().enable_regex) { - CEL_RETURN_IF_ERROR(RegisterRegexExtensionFunctions( - builder.function_registry(), - /*disable_extract=*/false, - runtime.expr_builder().options().regex_max_program_size)); - } - return absl::OkStatus(); -} absl::Status RegisterRegexExtensionFunctions( google::api::expr::runtime::CelFunctionRegistry* registry, const google::api::expr::runtime::InterpreterOptions& options) { if (options.enable_regex) { - return RegisterRegexExtensionFunctions(registry->InternalGetRegistry(), - /*disable_extract=*/true, - options.regex_max_program_size); + return regex_internal::RegisterRegexExtensionFunctions( + registry->InternalGetRegistry(), + /*disable_extract=*/true, options.regex_max_program_size); } return absl::OkStatus(); } -CheckerLibrary RegexExtCheckerLibrary() { - return {.id = "cel.lib.ext.regex", .configure = RegisterRegexCheckerDecls}; -} - -CompilerLibrary RegexExtCompilerLibrary() { - return CompilerLibrary::FromCheckerLibrary(RegexExtCheckerLibrary()); -} - -Validation RegexExtValidator() { - return RegexPatternValidator( - /*id=*/"", - {{"regex.extract", 1}, {"regex.extractAll", 1}, {"regex.replace", 1}}); -} - } // namespace cel::extensions diff --git a/extensions/regex_ext.h b/extensions/regex_ext.h index 7b32aee00..087d4a4f8 100644 --- a/extensions/regex_ext.h +++ b/extensions/regex_ext.h @@ -12,76 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -// This extension depends on the CEL optional type. Please ensure that the -// EnableOptionalTypes is called when using regex extensions. -// -// # Replace -// -// The `regex.replace` function replaces all non-overlapping substring of a -// regex pattern in the target string with the given replacement string. -// Optionally, you can limit the number of replacements by providing a count -// argument. When the count is a negative number, the function acts as replace -// all. Only numeric (\N) capture group references are supported in the -// replacement string, with validation for correctness. Backslashed-escaped -// digits (\1 to \9) within the replacement argument can be used to insert text -// matching the corresponding parenthesized group in the regexp pattern. An -// error will be thrown for invalid regex or replace string. -// -// regex.replace(target: string, pattern: string, -// replacement: string) -> string -// regex.replace(target: string, pattern: string, -// replacement: string, count: int) -> string -// -// Examples: -// -// regex.replace('hello world hello', 'hello', 'hi') == 'hi world hi' -// regex.replace('banana', 'a', 'x', 0) == 'banana' -// regex.replace('banana', 'a', 'x', 1) == 'bxnana' -// regex.replace('banana', 'a', 'x', -12) == 'bxnxnx' -// regex.replace('foo bar', '(fo)o (ba)r', r'\2 \1') == 'ba fo' -// regex.replace('test', '(.)', r'\2') \\ Runtime Error invalid replace -// string regex.replace('foo bar', '(', '$2 $1') \\ Runtime Error invalid -// -// # Extract -// -// The `regex.extract` function returns the first match of a regex pattern in a -// string. If no match is found, it returns an optional none value. An error -// will be thrown for invalid regex or for multiple capture groups. -// -// regex.extract(target: string, pattern: string) -> optional -// -// Examples: -// -// regex.extract('item-A, item-B', 'item-(\\w+)') == optional.of('A') -// regex.extract('HELLO', 'hello') == optional.empty() -// regex.extract('testuser@testdomain', '(.*)@([^.]*)') // Runtime Error -// multiple capture group -// -// # Extract All -// -// The `regex.extractAll` function returns a list of all matches of a regex -// pattern in a target string. If no matches are found, it returns an empty -// list. An error will be thrown for invalid regex or for multiple capture -// groups. -// -// regex.extractAll(target: string, pattern: string) -> list -// -// Examples: -// -// regex.extractAll('id:123, id:456', 'id:\\d+') == ['id:123', 'id:456'] -// regex.extractAll('testuser@testdomain', '(.*)@([^.]*)') // Runtime Error -// multiple capture group - #ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_EXT_H_ #define THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_EXT_H_ #include "absl/status/status.h" -#include "checker/type_checker_builder.h" -#include "compiler/compiler.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "runtime/runtime_builder.h" -#include "validator/validator.h" +#include "extensions/regex_ext_modern.h" // IWYU pragma: export namespace cel::extensions { @@ -94,38 +31,5 @@ absl::Status RegisterRegexExtensionFunctions( google::api::expr::runtime::CelFunctionRegistry* registry, const google::api::expr::runtime::InterpreterOptions& options); -// Register extension functions for regular expressions. -absl::Status RegisterRegexExtensionFunctions(RuntimeBuilder& builder); - -// Type check declarations for the regex extension library. -// Provides decls for the following functions: -// -// regex.replace(target: str, pattern: str, replacement: str) -> str -// -// regex.replace(target: str, pattern: str, replacement: str, count: int) -> str -// -// regex.extract(target: str, pattern: str) -> optional -// -// regex.extractAll(target: str, pattern: str) -> list -CheckerLibrary RegexExtCheckerLibrary(); - -// Provides decls for the following functions: -// -// regex.replace(target: str, pattern: str, replacement: str) -> str -// -// regex.replace(target: str, pattern: str, replacement: str, count: int) -> str -// -// regex.extract(target: str, pattern: str) -> optional -// -// regex.extractAll(target: str, pattern: str) -> list -CompilerLibrary RegexExtCompilerLibrary(); - -// Returns a `Validation` that checks all calls to the CEL regex extension -// functions. -// -// It validates that if the pattern is a literal string, it is a valid regular -// expression. -Validation RegexExtValidator(); - } // namespace cel::extensions #endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_EXT_H_ diff --git a/extensions/regex_ext_modern.cc b/extensions/regex_ext_modern.cc new file mode 100644 index 000000000..0f802bbed --- /dev/null +++ b/extensions/regex_ext_modern.cc @@ -0,0 +1,381 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "extensions/regex_ext_modern.h" + +#include +#include +#include +#include +#include +#include + +#include "absl/base/no_destructor.h" +#include "absl/base/nullability.h" +#include "absl/functional/bind_front.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "checker/internal/builtins_arena.h" +#include "checker/type_checker_builder.h" +#include "common/decl.h" +#include "common/type.h" +#include "common/value.h" +#include "compiler/compiler.h" +#include "internal/casts.h" +#include "internal/re2_options.h" +#include "internal/status_macros.h" +#include "runtime/function_adapter.h" +#include "runtime/function_registry.h" +#include "runtime/internal/runtime_friend_access.h" +#include "runtime/internal/runtime_impl.h" +#include "runtime/runtime_builder.h" +#include "validator/regex_validator.h" +#include "validator/validator.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" +#include "re2/re2.h" + +namespace cel::extensions { +namespace { + +using ::cel::checker_internal::BuiltinsArena; + +Value Extract(int regex_max_program_size, const StringValue& target, + const StringValue& regex, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + std::string target_scratch; + std::string regex_scratch; + absl::string_view target_view = target.ToStringView(&target_scratch); + absl::string_view regex_view = regex.ToStringView(®ex_scratch); + RE2 re2(regex_view, cel::internal::MakeRE2Options()); + CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) + .With(ErrorValueReturn(arena)); + const int group_count = re2.NumberOfCapturingGroups(); + if (group_count > 1) { + return ErrorValue::From( + absl::InvalidArgumentError(absl::StrFormat( + "regular expression has more than one capturing group: %s", + regex_view)), + arena); + } + + // Space for the full match (\0) and the first capture group (\1). + absl::string_view submatches[2]; + if (re2.Match(target_view, 0, target_view.length(), RE2::UNANCHORED, + submatches, 2)) { + // Return the capture group if it exists else return the full match. + const absl::string_view result_view = + (group_count == 1) ? submatches[1] : submatches[0]; + return OptionalValue::Of(StringValue::From(result_view, arena), arena); + } + + return OptionalValue::None(); +} + +Value ExtractAll(int regex_max_program_size, const StringValue& target, + const StringValue& regex, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + std::string target_scratch; + std::string regex_scratch; + absl::string_view target_view = target.ToStringView(&target_scratch); + absl::string_view regex_view = regex.ToStringView(®ex_scratch); + RE2 re2(regex_view, cel::internal::MakeRE2Options()); + CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) + .With(ErrorValueReturn(arena)); + const int group_count = re2.NumberOfCapturingGroups(); + if (group_count > 1) { + return ErrorValue::From( + absl::InvalidArgumentError(absl::StrFormat( + "regular expression has more than one capturing group: %s", + regex_view)), + arena); + } + + auto builder = NewListValueBuilder(arena); + absl::string_view temp_target = target_view; + + // Space for the full match (\0) and the first capture group (\1). + absl::string_view submatches[2]; + const int group_to_extract = (group_count == 1) ? 1 : 0; + + while (re2.Match(temp_target, 0, temp_target.length(), RE2::UNANCHORED, + submatches, group_count + 1)) { + const absl::string_view& full_match = submatches[0]; + const absl::string_view& desired_capture = submatches[group_to_extract]; + + // Avoid infinite loops on zero-length matches + if (full_match.empty()) { + if (temp_target.empty()) { + break; + } + temp_target.remove_prefix(1); + continue; + } + + if (group_count == 1 && desired_capture.empty()) { + temp_target.remove_prefix(full_match.data() - temp_target.data() + + full_match.length()); + continue; + } + + absl::Status status = + builder->Add(StringValue::From(desired_capture, arena)); + if (!status.ok()) { + return ErrorValue::From(status, arena); + } + temp_target.remove_prefix(full_match.data() - temp_target.data() + + full_match.length()); + } + + return std::move(*builder).Build(); +} + +Value ReplaceAll(int regex_max_program_size, const StringValue& target, + const StringValue& regex, const StringValue& replacement, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + std::string target_scratch; + std::string regex_scratch; + std::string replacement_scratch; + absl::string_view target_view = target.ToStringView(&target_scratch); + absl::string_view regex_view = regex.ToStringView(®ex_scratch); + absl::string_view replacement_view = + replacement.ToStringView(&replacement_scratch); + RE2 re2(regex_view, cel::internal::MakeRE2Options()); + CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) + .With(ErrorValueReturn(arena)); + std::string error_string; + if (!re2.CheckRewriteString(replacement_view, &error_string)) { + return ErrorValue::From( + absl::InvalidArgumentError( + absl::StrFormat("invalid replacement string: %s", error_string)), + arena); + } + + std::string output(target_view); + RE2::GlobalReplace(&output, re2, replacement_view); + + return StringValue::From(std::move(output), arena); +} + +Value ReplaceN(int regex_max_program_size, const StringValue& target, + const StringValue& regex, const StringValue& replacement, + int64_t count, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + if (count == 0) { + return target; + } + if (count < 0) { + return ReplaceAll(regex_max_program_size, target, regex, replacement, + descriptor_pool, message_factory, arena); + } + + std::string target_scratch; + std::string regex_scratch; + std::string replacement_scratch; + absl::string_view target_view = target.ToStringView(&target_scratch); + absl::string_view regex_view = regex.ToStringView(®ex_scratch); + absl::string_view replacement_view = + replacement.ToStringView(&replacement_scratch); + RE2 re2(regex_view, cel::internal::MakeRE2Options()); + CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) + .With(ErrorValueReturn(arena)); + std::string error_string; + if (!re2.CheckRewriteString(replacement_view, &error_string)) { + return ErrorValue::From( + absl::InvalidArgumentError( + absl::StrFormat("invalid replacement string: %s", error_string)), + arena); + } + + std::string output; + size_t startpos = 0; + std::optional last_match_end; + int64_t replaced_count = 0; + // RE2's Rewrite only supports substitutions for groups \0 through \9. + absl::string_view match[10]; + int nmatch = std::min(9, re2.NumberOfCapturingGroups()) + 1; + + while (startpos <= target_view.length() && replaced_count < count && + re2.Match(target_view, startpos, target_view.length(), RE2::UNANCHORED, + match, nmatch)) { + absl::string_view full_match = match[0]; + size_t match_start = + target_view.empty() + ? 0 + : static_cast(full_match.data() - target_view.data()); + + if (match_start > startpos) { + output.append(target_view.data() + startpos, match_start - startpos); + } + + if (full_match.empty() && last_match_end == match_start) { + if (match_start >= target_view.length()) { + break; + } + // Disallow empty match at end of last match: skip ahead one UTF-8 rune + // so anchors like `^` cannot re-match on a sliced prefix. + size_t step = 1; + while (match_start + step < target_view.length() && + (static_cast(target_view[match_start + step]) & + 0xC0) == 0x80) { + ++step; + } + output.append(target_view.data() + match_start, step); + startpos = match_start + step; + continue; + } + + if (!re2.Rewrite(&output, replacement_view, match, nmatch)) { + // This should ideally not happen given CheckRewriteString passed + return ErrorValue::From( + absl::InternalError("rewrite failed unexpectedly"), arena); + } + + startpos = match_start + full_match.length(); + last_match_end = startpos; + replaced_count++; + } + + if (startpos < target_view.length()) { + output.append(target_view.data() + startpos, + target_view.length() - startpos); + } + + return StringValue::From(std::move(output), arena); +} + +const Type& OptionalStringType() { + static absl::NoDestructor kInstance( + OptionalType(BuiltinsArena(), StringType())); + return *kInstance; +} + +const Type& ListStringType() { + static absl::NoDestructor kInstance( + ListType(BuiltinsArena(), StringType())); + return *kInstance; +} + +absl::Status RegisterRegexCheckerDecls(TypeCheckerBuilder& builder) { + CEL_ASSIGN_OR_RETURN( + FunctionDecl extract_decl, + MakeFunctionDecl( + "regex.extract", + MakeOverloadDecl("regex_extract_string_string", OptionalStringType(), + StringType(), StringType()))); + + CEL_ASSIGN_OR_RETURN( + FunctionDecl extract_all_decl, + MakeFunctionDecl( + "regex.extractAll", + MakeOverloadDecl("regex_extractAll_string_string", ListStringType(), + StringType(), StringType()))); + + CEL_ASSIGN_OR_RETURN( + FunctionDecl replace_decl, + MakeFunctionDecl( + "regex.replace", + MakeOverloadDecl("regex_replace_string_string_string", StringType(), + StringType(), StringType(), StringType()), + MakeOverloadDecl("regex_replace_string_string_string_int", + StringType(), StringType(), StringType(), + StringType(), IntType()))); + + CEL_RETURN_IF_ERROR(builder.AddFunction(extract_decl)); + CEL_RETURN_IF_ERROR(builder.AddFunction(extract_all_decl)); + CEL_RETURN_IF_ERROR(builder.AddFunction(replace_decl)); + return absl::OkStatus(); +} + +} // namespace + +namespace regex_internal { + +absl::Status RegisterRegexExtensionFunctions(FunctionRegistry& registry, + bool disable_extract, + int regex_max_program_size) { + if (!disable_extract) { + CEL_RETURN_IF_ERROR(( + BinaryFunctionAdapter, StringValue, StringValue>:: + RegisterGlobalOverload( + "regex.extract", + absl::bind_front(&Extract, regex_max_program_size), registry))); + } + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter, StringValue, StringValue>:: + RegisterGlobalOverload( + "regex.extractAll", + absl::bind_front(&ExtractAll, regex_max_program_size), + registry))); + CEL_RETURN_IF_ERROR( + (TernaryFunctionAdapter< + absl::StatusOr, StringValue, StringValue, + StringValue>::RegisterGlobalOverload("regex.replace", + absl::bind_front( + &ReplaceAll, + regex_max_program_size), + registry))); + CEL_RETURN_IF_ERROR( + (QuaternaryFunctionAdapter, StringValue, + StringValue, StringValue, int64_t>:: + RegisterGlobalOverload( + "regex.replace", + absl::bind_front(&ReplaceN, regex_max_program_size), registry))); + return absl::OkStatus(); +} + +} // namespace regex_internal + +absl::Status RegisterRegexExtensionFunctions(RuntimeBuilder& builder) { + auto& runtime = cel::internal::down_cast( + runtime_internal::RuntimeFriendAccess::GetMutableRuntime(builder)); + if (!runtime.expr_builder().optional_types_enabled()) { + return absl::InvalidArgumentError( + "regex extensions requires the optional types to be enabled"); + } + if (runtime.expr_builder().options().enable_regex) { + CEL_RETURN_IF_ERROR(regex_internal::RegisterRegexExtensionFunctions( + builder.function_registry(), + /*disable_extract=*/false, + runtime.expr_builder().options().regex_max_program_size)); + } + return absl::OkStatus(); +} + +CheckerLibrary RegexExtCheckerLibrary() { + return {.id = "cel.lib.ext.regex", .configure = RegisterRegexCheckerDecls}; +} + +CompilerLibrary RegexExtCompilerLibrary() { + return CompilerLibrary::FromCheckerLibrary(RegexExtCheckerLibrary()); +} + +Validation RegexExtValidator() { + return RegexPatternValidator( + /*id=*/"", + {{"regex.extract", 1}, {"regex.extractAll", 1}, {"regex.replace", 1}}); +} + +} // namespace cel::extensions diff --git a/extensions/regex_ext_modern.h b/extensions/regex_ext_modern.h new file mode 100644 index 000000000..3c6c60f4e --- /dev/null +++ b/extensions/regex_ext_modern.h @@ -0,0 +1,129 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This extension depends on the CEL optional type. Please ensure that the +// EnableOptionalTypes is called when using regex extensions. +// +// # Replace +// +// The `regex.replace` function replaces all non-overlapping substring of a +// regex pattern in the target string with the given replacement string. +// Optionally, you can limit the number of replacements by providing a count +// argument. When the count is a negative number, the function acts as replace +// all. Only numeric (\N) capture group references are supported in the +// replacement string, with validation for correctness. Backslashed-escaped +// digits (\1 to \9) within the replacement argument can be used to insert text +// matching the corresponding parenthesized group in the regexp pattern. An +// error will be thrown for invalid regex or replace string. +// +// regex.replace(target: string, pattern: string, +// replacement: string) -> string +// regex.replace(target: string, pattern: string, +// replacement: string, count: int) -> string +// +// Examples: +// +// regex.replace('hello world hello', 'hello', 'hi') == 'hi world hi' +// regex.replace('banana', 'a', 'x', 0) == 'banana' +// regex.replace('banana', 'a', 'x', 1) == 'bxnana' +// regex.replace('banana', 'a', 'x', -12) == 'bxnxnx' +// regex.replace('foo bar', '(fo)o (ba)r', r'\2 \1') == 'ba fo' +// regex.replace('test', '(.)', r'\2') \\ Runtime Error invalid replace +// string regex.replace('foo bar', '(', '$2 $1') \\ Runtime Error invalid +// +// # Extract +// +// The `regex.extract` function returns the first match of a regex pattern in a +// string. If no match is found, it returns an optional none value. An error +// will be thrown for invalid regex or for multiple capture groups. +// +// regex.extract(target: string, pattern: string) -> optional +// +// Examples: +// +// regex.extract('item-A, item-B', 'item-(\\w+)') == optional.of('A') +// regex.extract('HELLO', 'hello') == optional.empty() +// regex.extract('testuser@testdomain', '(.*)@([^.]*)') // Runtime Error +// multiple capture group +// +// # Extract All +// +// The `regex.extractAll` function returns a list of all matches of a regex +// pattern in a target string. If no matches are found, it returns an empty +// list. An error will be thrown for invalid regex or for multiple capture +// groups. +// +// regex.extractAll(target: string, pattern: string) -> list +// +// Examples: +// +// regex.extractAll('id:123, id:456', 'id:\\d+') == ['id:123', 'id:456'] +// regex.extractAll('testuser@testdomain', '(.*)@([^.]*)') // Runtime Error +// multiple capture group + +#ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_EXT_MODERN_H_ +#define THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_EXT_MODERN_H_ + +#include "absl/status/status.h" +#include "checker/type_checker_builder.h" +#include "compiler/compiler.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_builder.h" +#include "validator/validator.h" + +namespace cel::extensions { + +// Register extension functions for regular expressions. +absl::Status RegisterRegexExtensionFunctions(RuntimeBuilder& builder); + +// Type check declarations for the regex extension library. +// Provides decls for the following functions: +// +// regex.replace(target: str, pattern: str, replacement: str) -> str +// +// regex.replace(target: str, pattern: str, replacement: str, count: int) -> str +// +// regex.extract(target: str, pattern: str) -> optional +// +// regex.extractAll(target: str, pattern: str) -> list +CheckerLibrary RegexExtCheckerLibrary(); + +// Provides decls for the following functions: +// +// regex.replace(target: str, pattern: str, replacement: str) -> str +// +// regex.replace(target: str, pattern: str, replacement: str, count: int) -> str +// +// regex.extract(target: str, pattern: str) -> optional +// +// regex.extractAll(target: str, pattern: str) -> list +CompilerLibrary RegexExtCompilerLibrary(); + +// Returns a `Validation` that checks all calls to the CEL regex extension +// functions. +// +// It validates that if the pattern is a literal string, it is a valid regular +// expression. +Validation RegexExtValidator(); + +namespace regex_internal { +// Internal helper shared with the legacy CelFunctionRegistry wrapper. +absl::Status RegisterRegexExtensionFunctions(FunctionRegistry& registry, + bool disable_extract, + int regex_max_program_size); +} // namespace regex_internal + +} // namespace cel::extensions + +#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_EXT_MODERN_H_ diff --git a/extensions/regex_ext_test.cc b/extensions/regex_ext_test.cc index 26d9936aa..2fa5d31a4 100644 --- a/extensions/regex_ext_test.cc +++ b/extensions/regex_ext_test.cc @@ -362,6 +362,12 @@ std::vector regexTestCases() { R"(regex.replace('123456789ABC', '(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\w)(\\w)(\\w)', r'\1-\9-X', 1) == '1-9-X')"}, + {EvaluationType::kBoolTrue, + "regex.replace('abc', '', '-', 2) == '-a-bc'"}, + {EvaluationType::kBoolTrue, + "regex.replace('abc', '^', '>', 2) == '>abc'"}, + {EvaluationType::kBoolTrue, + "regex.replace('abc', '$', '<', 2) == 'abc<'"}, // Static Errors {EvaluationType::kUnknownStaticError, "regex.replace('abc', '^', 1)", diff --git a/extensions/regex_functions.cc b/extensions/regex_functions.cc index 3935772a2..a2ff255ff 100644 --- a/extensions/regex_functions.cc +++ b/extensions/regex_functions.cc @@ -14,229 +14,20 @@ #include "extensions/regex_functions.h" -#include -#include -#include -#include - -#include "absl/base/no_destructor.h" -#include "absl/base/nullability.h" -#include "absl/functional/bind_front.h" #include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "checker/internal/builtins_arena.h" -#include "checker/type_checker_builder.h" -#include "common/decl.h" -#include "common/type.h" -#include "common/value.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "internal/re2_options.h" #include "internal/status_macros.h" -#include "runtime/function_adapter.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" -#include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" -#include "re2/re2.h" namespace cel::extensions { -namespace { - -using ::cel::checker_internal::BuiltinsArena; -using ::google::api::expr::runtime::CelFunctionRegistry; -using ::google::api::expr::runtime::InterpreterOptions; - -// Extract matched group values from the given target string and rewrite the -// string -Value ExtractString(int regex_max_program_size, const StringValue& target, - const StringValue& regex, const StringValue& rewrite, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - std::string regex_scratch; - std::string target_scratch; - std::string rewrite_scratch; - absl::string_view regex_view = regex.ToStringView(®ex_scratch); - absl::string_view target_view = target.ToStringView(&target_scratch); - absl::string_view rewrite_view = rewrite.ToStringView(&rewrite_scratch); - - RE2 re2(regex_view, cel::internal::MakeRE2Options()); - CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) - .With(ErrorValueReturn(arena)); - std::string output; - bool result = RE2::Extract(target_view, re2, rewrite_view, &output); - if (!result) { - return ErrorValue::From(absl::InvalidArgumentError( - "Unable to extract string for the given regex"), - arena); - } - return StringValue::From(std::move(output), arena); -} - -// Captures the first unnamed/named group value -// NOTE: For capturing all the groups, use CaptureStringN instead -Value CaptureString(int regex_max_program_size, const StringValue& target, - const StringValue& regex, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - std::string regex_scratch; - std::string target_scratch; - absl::string_view regex_view = regex.ToStringView(®ex_scratch); - absl::string_view target_view = target.ToStringView(&target_scratch); - RE2 re2(regex_view, cel::internal::MakeRE2Options()); - CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) - .With(ErrorValueReturn(arena)); - std::string output; - bool result = RE2::FullMatch(target_view, re2, &output); - if (!result) { - return ErrorValue::From(absl::InvalidArgumentError( - "Unable to capture groups for the given regex"), - arena); - } else { - return StringValue::From(std::move(output), arena); - } -} - -// Does a FullMatchN on the given string and regex and returns a map with pairs as follows: -// a. For a named group - -// b. For an unnamed group - -absl::StatusOr CaptureStringN( - int regex_max_program_size, const StringValue& target, - const StringValue& regex, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - std::string target_scratch; - std::string regex_scratch; - absl::string_view target_view = target.ToStringView(&target_scratch); - absl::string_view regex_view = regex.ToStringView(®ex_scratch); - RE2 re2(regex_view, cel::internal::MakeRE2Options()); - CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) - .With(ErrorValueReturn(arena)); - const int capturing_groups_count = re2.NumberOfCapturingGroups(); - const auto& named_capturing_groups_map = re2.CapturingGroupNames(); - if (capturing_groups_count <= 0) { - return ErrorValue::From( - absl::InvalidArgumentError( - "Capturing groups were not found in the given regex."), - arena); - } - std::vector captured_strings(capturing_groups_count); - std::vector captured_string_addresses(capturing_groups_count); - std::vector argv(capturing_groups_count); - for (int j = 0; j < capturing_groups_count; j++) { - captured_string_addresses[j] = &captured_strings[j]; - argv[j] = &captured_string_addresses[j]; - } - bool result = - RE2::FullMatchN(target_view, re2, argv.data(), capturing_groups_count); - if (!result) { - return ErrorValue::From(absl::InvalidArgumentError( - "Unable to capture groups for the given regex"), - arena); - } - auto builder = cel::NewMapValueBuilder(arena); - builder->Reserve(capturing_groups_count); - for (int index = 1; index <= capturing_groups_count; index++) { - auto it = named_capturing_groups_map.find(index); - std::string name = it != named_capturing_groups_map.end() - ? it->second - : std::to_string(index); - CEL_RETURN_IF_ERROR(builder->Put( - StringValue::From(std::move(name), arena), - StringValue::From(std::move(captured_strings[index - 1]), arena))); - } - return std::move(*builder).Build(); -} - -absl::Status RegisterRegexFunctions(FunctionRegistry& registry, - int max_regex_program_size) { - // Register Regex Extract Function - CEL_RETURN_IF_ERROR( - (TernaryFunctionAdapter< - absl::StatusOr, StringValue, StringValue, - StringValue>::RegisterGlobalOverload(kRegexExtract, - absl::bind_front( - &ExtractString, - max_regex_program_size), - registry))); - - // Register Regex Captures Function - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter, StringValue, StringValue>:: - RegisterGlobalOverload( - kRegexCapture, - absl::bind_front(&CaptureString, max_regex_program_size), - registry))); - - // Register Regex CaptureN Function - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter, StringValue, StringValue>:: - RegisterGlobalOverload( - kRegexCaptureN, - absl::bind_front(&CaptureStringN, max_regex_program_size), - registry))); - return absl::OkStatus(); -} - -const Type& CaptureNMapType() { - static absl::NoDestructor kInstance( - MapType(BuiltinsArena(), StringType(), StringType())); - return *kInstance; -} - -absl::Status RegisterRegexDecls(TypeCheckerBuilder& builder) { - CEL_ASSIGN_OR_RETURN( - FunctionDecl regex_extract_decl, - MakeFunctionDecl( - std::string(kRegexExtract), - MakeOverloadDecl("re_extract_string_string_string", StringType(), - StringType(), StringType(), StringType()))); - CEL_RETURN_IF_ERROR(builder.AddFunction(regex_extract_decl)); - - CEL_ASSIGN_OR_RETURN( - FunctionDecl regex_capture_decl, - MakeFunctionDecl( - std::string(kRegexCapture), - MakeOverloadDecl("re_capture_string_string", StringType(), - StringType(), StringType()))); - CEL_RETURN_IF_ERROR(builder.AddFunction(regex_capture_decl)); - - CEL_ASSIGN_OR_RETURN( - FunctionDecl regex_capture_n_decl, - MakeFunctionDecl( - std::string(kRegexCaptureN), - MakeOverloadDecl("re_captureN_string_string", CaptureNMapType(), - StringType(), StringType()))); - return builder.AddFunction(regex_capture_n_decl); -} -} // namespace - -absl::Status RegisterRegexFunctions(FunctionRegistry& registry, - const RuntimeOptions& options) { - if (options.enable_regex) { - CEL_RETURN_IF_ERROR( - RegisterRegexFunctions(registry, options.regex_max_program_size)); - } - return absl::OkStatus(); -} - -absl::Status RegisterRegexFunctions(CelFunctionRegistry* registry, - const InterpreterOptions& options) { +absl::Status RegisterRegexFunctions( + google::api::expr::runtime::CelFunctionRegistry* registry, + const google::api::expr::runtime::InterpreterOptions& options) { CEL_RETURN_IF_ERROR(RegisterRegexFunctions( registry->InternalGetRegistry(), google::api::expr::runtime::ConvertToRuntimeOptions(options))); return absl::OkStatus(); } -CheckerLibrary RegexCheckerLibrary() { - return {.id = "cpp_regex", .configure = RegisterRegexDecls}; -} - } // namespace cel::extensions diff --git a/extensions/regex_functions.h b/extensions/regex_functions.h index 62c83ebdd..1d5e546c9 100644 --- a/extensions/regex_functions.h +++ b/extensions/regex_functions.h @@ -21,19 +21,12 @@ #include "absl/base/attributes.h" #include "absl/status/status.h" -#include "absl/strings/string_view.h" -#include "checker/type_checker_builder.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" +#include "extensions/regex_functions_modern.h" // IWYU pragma: export namespace cel::extensions { -inline constexpr absl::string_view kRegexExtract = "re.extract"; -inline constexpr absl::string_view kRegexCapture = "re.capture"; -inline constexpr absl::string_view kRegexCaptureN = "re.captureN"; - // Register Extract and Capture Functions for RE2 // Requires options.enable_regex to be true // The canonical regex extensions supported by the CEL team are registered @@ -42,11 +35,6 @@ ABSL_DEPRECATED("Use RegisterRegexExtensionsFunctions instead.") absl::Status RegisterRegexFunctions( google::api::expr::runtime::CelFunctionRegistry* registry, const google::api::expr::runtime::InterpreterOptions& options); -absl::Status RegisterRegexFunctions(FunctionRegistry& registry, - const RuntimeOptions& options); - -// Declarations for the regex extension library. -CheckerLibrary RegexCheckerLibrary(); } // namespace cel::extensions #endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_FUNCTIONS_H_ diff --git a/extensions/regex_functions_modern.cc b/extensions/regex_functions_modern.cc new file mode 100644 index 000000000..38fa11e22 --- /dev/null +++ b/extensions/regex_functions_modern.cc @@ -0,0 +1,229 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "extensions/regex_functions_modern.h" + +#include +#include +#include + +#include "absl/base/no_destructor.h" +#include "absl/base/nullability.h" +#include "absl/functional/bind_front.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "checker/internal/builtins_arena.h" +#include "checker/type_checker_builder.h" +#include "common/decl.h" +#include "common/type.h" +#include "common/value.h" +#include "internal/re2_options.h" +#include "internal/status_macros.h" +#include "runtime/function_adapter.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" +#include "re2/re2.h" + +namespace cel::extensions { +namespace { + +using ::cel::checker_internal::BuiltinsArena; + +// Extract matched group values from the given target string and rewrite the +// string +Value ExtractString(int regex_max_program_size, const StringValue& target, + const StringValue& regex, const StringValue& rewrite, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + std::string regex_scratch; + std::string target_scratch; + std::string rewrite_scratch; + absl::string_view regex_view = regex.ToStringView(®ex_scratch); + absl::string_view target_view = target.ToStringView(&target_scratch); + absl::string_view rewrite_view = rewrite.ToStringView(&rewrite_scratch); + + RE2 re2(regex_view, cel::internal::MakeRE2Options()); + CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) + .With(ErrorValueReturn(arena)); + std::string output; + bool result = RE2::Extract(target_view, re2, rewrite_view, &output); + if (!result) { + return ErrorValue::From(absl::InvalidArgumentError( + "Unable to extract string for the given regex"), + arena); + } + return StringValue::From(std::move(output), arena); +} + +// Captures the first unnamed/named group value +// NOTE: For capturing all the groups, use CaptureStringN instead +Value CaptureString(int regex_max_program_size, const StringValue& target, + const StringValue& regex, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + std::string regex_scratch; + std::string target_scratch; + absl::string_view regex_view = regex.ToStringView(®ex_scratch); + absl::string_view target_view = target.ToStringView(&target_scratch); + RE2 re2(regex_view, cel::internal::MakeRE2Options()); + CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) + .With(ErrorValueReturn(arena)); + std::string output; + bool result = RE2::FullMatch(target_view, re2, &output); + if (!result) { + return ErrorValue::From(absl::InvalidArgumentError( + "Unable to capture groups for the given regex"), + arena); + } + return StringValue::From(std::move(output), arena); +} + +// Does a FullMatchN on the given string and regex and returns a map with pairs as follows: +// a. For a named group - +// b. For an unnamed group - +absl::StatusOr CaptureStringN( + int regex_max_program_size, const StringValue& target, + const StringValue& regex, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + std::string target_scratch; + std::string regex_scratch; + absl::string_view target_view = target.ToStringView(&target_scratch); + absl::string_view regex_view = regex.ToStringView(®ex_scratch); + RE2 re2(regex_view, cel::internal::MakeRE2Options()); + CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, regex_max_program_size)) + .With(ErrorValueReturn(arena)); + const int capturing_groups_count = re2.NumberOfCapturingGroups(); + const auto& named_capturing_groups_map = re2.CapturingGroupNames(); + if (capturing_groups_count <= 0) { + return ErrorValue::From( + absl::InvalidArgumentError( + "Capturing groups were not found in the given regex."), + arena); + } + std::vector captured_strings(capturing_groups_count); + std::vector captured_string_addresses(capturing_groups_count); + std::vector argv(capturing_groups_count); + for (int j = 0; j < capturing_groups_count; j++) { + captured_string_addresses[j] = &captured_strings[j]; + argv[j] = &captured_string_addresses[j]; + } + bool result = + RE2::FullMatchN(target_view, re2, argv.data(), capturing_groups_count); + if (!result) { + return ErrorValue::From(absl::InvalidArgumentError( + "Unable to capture groups for the given regex"), + arena); + } + cel::MapValueBuilderPtr builder = cel::NewMapValueBuilder(arena); + builder->Reserve(capturing_groups_count); + for (int index = 1; index <= capturing_groups_count; index++) { + auto it = named_capturing_groups_map.find(index); + std::string name = it != named_capturing_groups_map.end() + ? it->second + : absl::StrCat(index); + CEL_RETURN_IF_ERROR(builder->Put( + StringValue::From(std::move(name), arena), + StringValue::From(std::move(captured_strings[index - 1]), arena))); + } + return std::move(*builder).Build(); +} + +absl::Status RegisterRegexFunctions(FunctionRegistry& registry, + int max_regex_program_size) { + // Register Regex Extract Function + CEL_RETURN_IF_ERROR( + (TernaryFunctionAdapter< + absl::StatusOr, StringValue, StringValue, + StringValue>::RegisterGlobalOverload(kRegexExtract, + absl::bind_front( + &ExtractString, + max_regex_program_size), + registry))); + + // Register Regex Captures Function + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter, StringValue, StringValue>:: + RegisterGlobalOverload( + kRegexCapture, + absl::bind_front(&CaptureString, max_regex_program_size), + registry))); + + // Register Regex CaptureN Function + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter, StringValue, StringValue>:: + RegisterGlobalOverload( + kRegexCaptureN, + absl::bind_front(&CaptureStringN, max_regex_program_size), + registry))); + return absl::OkStatus(); +} + +const Type& CaptureNMapType() { + static absl::NoDestructor kInstance( + MapType(BuiltinsArena(), StringType(), StringType())); + return *kInstance; +} + +absl::Status RegisterRegexDecls(TypeCheckerBuilder& builder) { + CEL_ASSIGN_OR_RETURN( + FunctionDecl regex_extract_decl, + MakeFunctionDecl( + std::string(kRegexExtract), + MakeOverloadDecl("re_extract_string_string_string", StringType(), + StringType(), StringType(), StringType()))); + CEL_RETURN_IF_ERROR(builder.AddFunction(regex_extract_decl)); + + CEL_ASSIGN_OR_RETURN( + FunctionDecl regex_capture_decl, + MakeFunctionDecl( + std::string(kRegexCapture), + MakeOverloadDecl("re_capture_string_string", StringType(), + StringType(), StringType()))); + CEL_RETURN_IF_ERROR(builder.AddFunction(regex_capture_decl)); + + CEL_ASSIGN_OR_RETURN( + FunctionDecl regex_capture_n_decl, + MakeFunctionDecl( + std::string(kRegexCaptureN), + MakeOverloadDecl("re_captureN_string_string", CaptureNMapType(), + StringType(), StringType()))); + return builder.AddFunction(regex_capture_n_decl); +} + +} // namespace + +absl::Status RegisterRegexFunctions(FunctionRegistry& registry, + const RuntimeOptions& options) { + if (options.enable_regex) { + CEL_RETURN_IF_ERROR( + RegisterRegexFunctions(registry, options.regex_max_program_size)); + } + return absl::OkStatus(); +} + +CheckerLibrary RegexCheckerLibrary() { + return {.id = "cpp_regex", .configure = RegisterRegexDecls}; +} + +} // namespace cel::extensions diff --git a/extensions/regex_functions_modern.h b/extensions/regex_functions_modern.h new file mode 100644 index 000000000..c8dfeb9e0 --- /dev/null +++ b/extensions/regex_functions_modern.h @@ -0,0 +1,46 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Definitions for extension functions wrapping C++ RE2 APIs. These are +// only defined for the C++ CEL library and distinct from the regex +// extension library (supported by other implementations). + +#ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_FUNCTIONS_MODERN_H_ +#define THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_FUNCTIONS_MODERN_H_ + +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "checker/type_checker_builder.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" + +namespace cel::extensions { + +inline constexpr absl::string_view kRegexExtract = "re.extract"; +inline constexpr absl::string_view kRegexCapture = "re.capture"; +inline constexpr absl::string_view kRegexCaptureN = "re.captureN"; + +// Register Extract and Capture Functions for RE2 +// Requires options.enable_regex to be true +// The canonical regex extensions supported by the CEL team are registered +// via the `RegisterRegexExtensionsFunctions`. This extension is deprecated. +absl::Status RegisterRegexFunctions(FunctionRegistry& registry, + const RuntimeOptions& options); + +// Declarations for the regex extension library. +CheckerLibrary RegexCheckerLibrary(); + +} // namespace cel::extensions + +#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_REGEX_FUNCTIONS_MODERN_H_ diff --git a/extensions/select_optimization.cc b/extensions/select_optimization.cc index 97146d1a6..da47ecb43 100644 --- a/extensions/select_optimization.cc +++ b/extensions/select_optimization.cc @@ -47,7 +47,6 @@ #include "common/function_descriptor.h" #include "common/internal/attribute_trail.h" #include "common/kind.h" -#include "common/legacy_value.h" #include "common/native_type.h" #include "common/type.h" #include "common/value.h" @@ -838,10 +837,9 @@ absl::Status OptimizedSelectImpl::ApplySelect(ExecutionFrameBase& frame, if (result_in_out.IsParsedMessage()) { ParsedMessageValue parsed_message = result_in_out.GetParsedMessage(); current_message = parsed_message.message(); - is_unsafe = - cel::interop_internal::IsUnsafeParsedMessageValue(parsed_message); + is_unsafe = parsed_message.is_unsafe(); } else if (const google::protobuf::Message* legacy_message = - cel::interop_internal::GetLegacyMessage(result_in_out); + cel::common_internal::GetLegacyMessage(result_in_out); legacy_message != nullptr) { current_message = legacy_message; is_unsafe = true; diff --git a/extensions/select_optimization_test.cc b/extensions/select_optimization_test.cc index d6efb8de6..ed79cfb47 100644 --- a/extensions/select_optimization_test.cc +++ b/extensions/select_optimization_test.cc @@ -60,7 +60,6 @@ #include "eval/compiler/resolver.h" #include "eval/eval/evaluator_core.h" #include "eval/internal/interop.h" -#include "eval/public/cel_type_registry.h" #include "eval/public/cel_value.h" #include "eval/public/structs/cel_proto_wrapper.h" #include "extensions/protobuf/ast_converters.h" @@ -352,7 +351,6 @@ class SelectOptimizationTest : public testing::Test { public: SelectOptimizationTest() : env_(NewTestingRuntimeEnv()), - legacy_registry_(env_->legacy_type_registry), type_registry_(env_->type_registry), function_registry_(env_->function_registry), resolver_("", function_registry_, type_registry_, @@ -377,7 +375,6 @@ class SelectOptimizationTest : public testing::Test { protected: absl_nonnull std::shared_ptr env_; - google::api::expr::runtime::CelTypeRegistry& legacy_registry_; TypeRegistry& type_registry_; FunctionRegistry& function_registry_; google::protobuf::Arena arena_; diff --git a/extensions/sets_functions.cc b/extensions/sets_functions.cc index ebe163550..de3dce8f3 100644 --- a/extensions/sets_functions.cc +++ b/extensions/sets_functions.cc @@ -14,158 +14,18 @@ #include "extensions/sets_functions.h" -#include "absl/base/nullability.h" #include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "base/function_adapter.h" -#include "checker/type_checker_builder.h" -#include "common/decl.h" -#include "common/type.h" -#include "common/value.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "internal/status_macros.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" -#include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" namespace cel::extensions { -using google::api::expr::runtime::CelFunctionRegistry; -using google::api::expr::runtime::ConvertToRuntimeOptions; -using google::api::expr::runtime::InterpreterOptions; -namespace { - -absl::StatusOr SetsContains( - const ListValue& list, const ListValue& sublist, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - bool any_missing = false; - CEL_RETURN_IF_ERROR(sublist.ForEach( - [&](const Value& sublist_element) -> absl::StatusOr { - CEL_ASSIGN_OR_RETURN(auto contains, - list.Contains(sublist_element, descriptor_pool, - message_factory, arena)); - - // Treat CEL error as missing - any_missing = - !contains->Is() || !contains.GetBool().NativeValue(); - // The first false result will terminate the loop. - return !any_missing; - }, - descriptor_pool, message_factory, arena)); - return BoolValue(!any_missing); -} - -absl::StatusOr SetsIntersects( - const ListValue& list, const ListValue& sublist, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - bool exists = false; - CEL_RETURN_IF_ERROR(list.ForEach( - [&](const Value& list_element) -> absl::StatusOr { - CEL_ASSIGN_OR_RETURN(auto contains, - sublist.Contains(list_element, descriptor_pool, - message_factory, arena)); - // Treat contains return CEL error as false for the sake of - // intersecting. - exists = contains->Is() && contains.GetBool().NativeValue(); - return !exists; - }, - descriptor_pool, message_factory, arena)); - - return BoolValue(exists); -} - -absl::StatusOr SetsEquivalent( - const ListValue& list, const ListValue& sublist, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - CEL_ASSIGN_OR_RETURN( - auto contains_sublist, - SetsContains(list, sublist, descriptor_pool, message_factory, arena)); - if (contains_sublist.Is() && - !contains_sublist.GetBool().NativeValue()) { - return contains_sublist; - } - return SetsContains(sublist, list, descriptor_pool, message_factory, arena); -} - -absl::Status RegisterSetsContainsFunction(FunctionRegistry& registry) { - return registry.Register( - BinaryFunctionAdapter< - absl::StatusOr, const ListValue&, - const ListValue&>::CreateDescriptor("sets.contains", - /*receiver_style=*/false), - BinaryFunctionAdapter, const ListValue&, - const ListValue&>::WrapFunction(SetsContains)); -} - -absl::Status RegisterSetsIntersectsFunction(FunctionRegistry& registry) { - return registry.Register( - BinaryFunctionAdapter< - absl::StatusOr, const ListValue&, - const ListValue&>::CreateDescriptor("sets.intersects", - /*receiver_style=*/false), - BinaryFunctionAdapter, const ListValue&, - const ListValue&>::WrapFunction(SetsIntersects)); -} - -absl::Status RegisterSetsEquivalentFunction(FunctionRegistry& registry) { - return registry.Register( - BinaryFunctionAdapter< - absl::StatusOr, const ListValue&, - const ListValue&>::CreateDescriptor("sets.equivalent", - /*receiver_style=*/false), - BinaryFunctionAdapter, const ListValue&, - const ListValue&>::WrapFunction(SetsEquivalent)); -} - -absl::Status RegisterSetsDecls(TypeCheckerBuilder& b) { - ListType list_t(b.arena(), TypeParamType("T")); - CEL_ASSIGN_OR_RETURN( - auto decl, - MakeFunctionDecl("sets.contains", - MakeOverloadDecl("list_sets_contains_list", BoolType(), - list_t, list_t))); - CEL_RETURN_IF_ERROR(b.AddFunction(decl)); - - CEL_ASSIGN_OR_RETURN( - decl, MakeFunctionDecl("sets.equivalent", - MakeOverloadDecl("list_sets_equivalent_list", - BoolType(), list_t, list_t))); - CEL_RETURN_IF_ERROR(b.AddFunction(decl)); - - CEL_ASSIGN_OR_RETURN( - decl, MakeFunctionDecl("sets.intersects", - MakeOverloadDecl("list_sets_intersects_list", - BoolType(), list_t, list_t))); - return b.AddFunction(decl); -} - -} // namespace - -CheckerLibrary SetsCheckerLibrary() { - return {.id = "cel.lib.ext.sets", .configure = RegisterSetsDecls}; -} - -absl::Status RegisterSetsFunctions(FunctionRegistry& registry, - const RuntimeOptions& options) { - CEL_RETURN_IF_ERROR(RegisterSetsContainsFunction(registry)); - CEL_RETURN_IF_ERROR(RegisterSetsIntersectsFunction(registry)); - CEL_RETURN_IF_ERROR(RegisterSetsEquivalentFunction(registry)); - return absl::OkStatus(); -} - -absl::Status RegisterSetsFunctions(CelFunctionRegistry* registry, - const InterpreterOptions& options) { - return RegisterSetsFunctions(registry->InternalGetRegistry(), - ConvertToRuntimeOptions(options)); +absl::Status RegisterSetsFunctions( + google::api::expr::runtime::CelFunctionRegistry* registry, + const google::api::expr::runtime::InterpreterOptions& options) { + return RegisterSetsFunctions( + registry->InternalGetRegistry(), + google::api::expr::runtime::ConvertToRuntimeOptions(options)); } } // namespace cel::extensions diff --git a/extensions/sets_functions.h b/extensions/sets_functions.h index a49e52174..a9d01de8d 100644 --- a/extensions/sets_functions.h +++ b/extensions/sets_functions.h @@ -16,26 +16,12 @@ #define THIRD_PARTY_CEL_CPP_EXTENSIONS_SETS_FUNCTIONS_H_ #include "absl/status/status.h" -#include "checker/type_checker_builder.h" -#include "compiler/compiler.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" +#include "extensions/sets_functions_modern.h" // IWYU pragma: export namespace cel::extensions { -// Declarations for the sets functions. -CheckerLibrary SetsCheckerLibrary(); - -inline CompilerLibrary SetsCompilerLibrary() { - return CompilerLibrary::FromCheckerLibrary(SetsCheckerLibrary()); -} - -// Register set functions. -absl::Status RegisterSetsFunctions(FunctionRegistry& registry, - const RuntimeOptions& options); - absl::Status RegisterSetsFunctions( google::api::expr::runtime::CelFunctionRegistry* registry, const google::api::expr::runtime::InterpreterOptions& options); diff --git a/extensions/sets_functions_modern.cc b/extensions/sets_functions_modern.cc new file mode 100644 index 000000000..e5faf7b83 --- /dev/null +++ b/extensions/sets_functions_modern.cc @@ -0,0 +1,160 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "extensions/sets_functions_modern.h" + +#include "absl/base/nullability.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "base/function_adapter.h" +#include "checker/type_checker_builder.h" +#include "common/decl.h" +#include "common/type.h" +#include "common/value.h" +#include "internal/status_macros.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" + +namespace cel::extensions { + +namespace { + +absl::StatusOr SetsContains( + const ListValue& list, const ListValue& sublist, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + bool any_missing = false; + CEL_RETURN_IF_ERROR(sublist.ForEach( + [&](const Value& sublist_element) -> absl::StatusOr { + CEL_ASSIGN_OR_RETURN(auto contains, + list.Contains(sublist_element, descriptor_pool, + message_factory, arena)); + + // Treat CEL error as missing + any_missing = + !contains->Is() || !contains.GetBool().NativeValue(); + // The first false result will terminate the loop. + return !any_missing; + }, + descriptor_pool, message_factory, arena)); + return BoolValue(!any_missing); +} + +absl::StatusOr SetsIntersects( + const ListValue& list, const ListValue& sublist, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + bool exists = false; + CEL_RETURN_IF_ERROR(list.ForEach( + [&](const Value& list_element) -> absl::StatusOr { + CEL_ASSIGN_OR_RETURN(auto contains, + sublist.Contains(list_element, descriptor_pool, + message_factory, arena)); + // Treat contains return CEL error as false for the sake of + // intersecting. + exists = contains->Is() && contains.GetBool().NativeValue(); + return !exists; + }, + descriptor_pool, message_factory, arena)); + + return BoolValue(exists); +} + +absl::StatusOr SetsEquivalent( + const ListValue& list, const ListValue& sublist, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + CEL_ASSIGN_OR_RETURN( + auto contains_sublist, + SetsContains(list, sublist, descriptor_pool, message_factory, arena)); + if (contains_sublist.Is() && + !contains_sublist.GetBool().NativeValue()) { + return contains_sublist; + } + return SetsContains(sublist, list, descriptor_pool, message_factory, arena); +} + +absl::Status RegisterSetsContainsFunction(FunctionRegistry& registry) { + return registry.Register( + BinaryFunctionAdapter< + absl::StatusOr, const ListValue&, + const ListValue&>::CreateDescriptor("sets.contains", + /*receiver_style=*/false), + BinaryFunctionAdapter, const ListValue&, + const ListValue&>::WrapFunction(SetsContains)); +} + +absl::Status RegisterSetsIntersectsFunction(FunctionRegistry& registry) { + return registry.Register( + BinaryFunctionAdapter< + absl::StatusOr, const ListValue&, + const ListValue&>::CreateDescriptor("sets.intersects", + /*receiver_style=*/false), + BinaryFunctionAdapter, const ListValue&, + const ListValue&>::WrapFunction(SetsIntersects)); +} + +absl::Status RegisterSetsEquivalentFunction(FunctionRegistry& registry) { + return registry.Register( + BinaryFunctionAdapter< + absl::StatusOr, const ListValue&, + const ListValue&>::CreateDescriptor("sets.equivalent", + /*receiver_style=*/false), + BinaryFunctionAdapter, const ListValue&, + const ListValue&>::WrapFunction(SetsEquivalent)); +} + +absl::Status RegisterSetsDecls(TypeCheckerBuilder& builder) { + ListType list_t(builder.arena(), TypeParamType("T")); + CEL_ASSIGN_OR_RETURN( + auto decl, + MakeFunctionDecl("sets.contains", + MakeOverloadDecl("list_sets_contains_list", BoolType(), + list_t, list_t))); + CEL_RETURN_IF_ERROR(builder.AddFunction(decl)); + + CEL_ASSIGN_OR_RETURN( + decl, MakeFunctionDecl("sets.equivalent", + MakeOverloadDecl("list_sets_equivalent_list", + BoolType(), list_t, list_t))); + CEL_RETURN_IF_ERROR(builder.AddFunction(decl)); + + CEL_ASSIGN_OR_RETURN( + decl, MakeFunctionDecl("sets.intersects", + MakeOverloadDecl("list_sets_intersects_list", + BoolType(), list_t, list_t))); + return builder.AddFunction(decl); +} + +} // namespace + +CheckerLibrary SetsCheckerLibrary() { + return {.id = "cel.lib.ext.sets", .configure = RegisterSetsDecls}; +} + +absl::Status RegisterSetsFunctions(FunctionRegistry& registry, + const RuntimeOptions& options) { + CEL_RETURN_IF_ERROR(RegisterSetsContainsFunction(registry)); + CEL_RETURN_IF_ERROR(RegisterSetsIntersectsFunction(registry)); + CEL_RETURN_IF_ERROR(RegisterSetsEquivalentFunction(registry)); + return absl::OkStatus(); +} + +} // namespace cel::extensions diff --git a/extensions/sets_functions_modern.h b/extensions/sets_functions_modern.h new file mode 100644 index 000000000..b420f2add --- /dev/null +++ b/extensions/sets_functions_modern.h @@ -0,0 +1,39 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_SETS_FUNCTIONS_MODERN_H_ +#define THIRD_PARTY_CEL_CPP_EXTENSIONS_SETS_FUNCTIONS_MODERN_H_ + +#include "absl/status/status.h" +#include "checker/type_checker_builder.h" +#include "compiler/compiler.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" + +namespace cel::extensions { + +// Declarations for the sets functions. +CheckerLibrary SetsCheckerLibrary(); + +inline CompilerLibrary SetsCompilerLibrary() { + return CompilerLibrary::FromCheckerLibrary(SetsCheckerLibrary()); +} + +// Register set functions. +absl::Status RegisterSetsFunctions(FunctionRegistry& registry, + const RuntimeOptions& options); + +} // namespace cel::extensions + +#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_SETS_FUNCTIONS_MODERN_H_ diff --git a/extensions/strings.cc b/extensions/strings.cc index 831600353..69b1a4de2 100644 --- a/extensions/strings.cc +++ b/extensions/strings.cc @@ -14,410 +14,12 @@ #include "extensions/strings.h" -#include -#include -#include - -#include "absl/base/no_destructor.h" -#include "absl/base/nullability.h" #include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/cord.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "checker/internal/builtins_arena.h" -#include "checker/type_checker_builder.h" -#include "common/decl.h" -#include "common/type.h" -#include "common/value.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "extensions/formatting.h" -#include "internal/status_macros.h" -#include "runtime/function.h" -#include "runtime/function_adapter.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" -#include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" namespace cel::extensions { -namespace { - -using ::cel::checker_internal::BuiltinsArena; - -struct AppendToStringVisitor { - std::string& append_to; - - void operator()(absl::string_view string) const { append_to.append(string); } - - void operator()(const absl::Cord& cord) const { - append_to.append(static_cast(cord)); - } -}; - -absl::StatusOr Join2( - const ListValue& value, const StringValue& separator, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - return separator.Join(value, descriptor_pool, message_factory, arena); -} - -absl::StatusOr Join1( - const ListValue& value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - return StringValue().Join(value, descriptor_pool, message_factory, arena); -} - -absl::StatusOr Split3( - const StringValue& string, const StringValue& delimiter, int64_t limit, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - return string.Split(delimiter, limit, arena); -} - -absl::StatusOr Split2( - const StringValue& string, const StringValue& delimiter, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - return string.Split(delimiter, arena); -} - -absl::StatusOr Replace2(const StringValue& string, - const StringValue& old_sub, - const StringValue& new_sub, int64_t limit, - const google::protobuf::DescriptorPool* absl_nonnull, - google::protobuf::MessageFactory* absl_nonnull, - google::protobuf::Arena* absl_nonnull arena) { - return string.Replace(old_sub, new_sub, limit, arena); -} - -absl::StatusOr Replace1( - const StringValue& string, const StringValue& old_sub, - const StringValue& new_sub, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - return string.Replace(old_sub, new_sub, -1, arena); -} - -Value CharAt(const StringValue& string, int64_t pos, - const Function::InvokeContext& context) { - return string.CharAt(pos, context.arena()); -} - -int64_t IndexOf2(const StringValue& haystack, const StringValue& needle) { - return haystack.IndexOf(needle).value_or(-1); -} - -Value IndexOf3(const StringValue& haystack, const StringValue& needle, - int64_t pos, const Function::InvokeContext& context) { - if (pos > haystack.Size()) { - return ErrorValue::From( - absl::InvalidArgumentError(absl::StrCat("index out of range: ", pos)), - context.arena()); - } - return IntValue(haystack.IndexOf(needle, pos).value_or(-1)); -} - -int64_t LastIndexOf2(const StringValue& haystack, const StringValue& needle) { - return haystack.LastIndexOf(needle).value_or(-1); -} - -Value LastIndexOf3(const StringValue& haystack, const StringValue& needle, - int64_t pos, const Function::InvokeContext& context) { - if (pos < 0 || pos > haystack.Size()) { - return ErrorValue::From( - absl::InvalidArgumentError(absl::StrCat("index out of range: ", pos)), - context.arena()); - } - return IntValue(haystack.LastIndexOf(needle, pos).value_or(-1)); -} - -Value Substring2(const StringValue& string, int64_t start, - const Function::InvokeContext& context) { - return string.Substring(start, context.arena()); -} - -Value Substring3(const StringValue& string, int64_t start, int64_t end, - const Function::InvokeContext& context) { - return string.Substring(start, end, context.arena()); -} - -StringValue Trim(const StringValue& string) { return string.Trim(); } - -StringValue LowerAscii(const StringValue& string, - const google::protobuf::DescriptorPool* absl_nonnull, - google::protobuf::MessageFactory* absl_nonnull, - google::protobuf::Arena* absl_nonnull arena) { - return string.LowerAscii(arena); -} - -StringValue UpperAscii(const StringValue& string, - const google::protobuf::DescriptorPool* absl_nonnull, - google::protobuf::MessageFactory* absl_nonnull, - google::protobuf::Arena* absl_nonnull arena) { - return string.UpperAscii(arena); -} - -StringValue Quote(const StringValue& string, - const google::protobuf::DescriptorPool* absl_nonnull, - google::protobuf::MessageFactory* absl_nonnull, - google::protobuf::Arena* absl_nonnull arena) { - return string.Quote(arena); -} - -StringValue Reverse(const StringValue& string, - const google::protobuf::DescriptorPool* absl_nonnull, - google::protobuf::MessageFactory* absl_nonnull, - google::protobuf::Arena* absl_nonnull arena) { - return string.Reverse(arena); -} - -const Type& ListStringType() { - static absl::NoDestructor kInstance( - ListType(BuiltinsArena(), StringType())); - return *kInstance; -} - -absl::Status RegisterStringsDecls(TypeCheckerBuilder& builder, int version) { - // Runtime Supported functions. - CEL_ASSIGN_OR_RETURN( - auto join_decl, - MakeFunctionDecl( - "join", - MakeMemberOverloadDecl("list_join", StringType(), ListStringType()), - MakeMemberOverloadDecl("list_join_string", StringType(), - ListStringType(), StringType()))); - CEL_ASSIGN_OR_RETURN( - auto split_decl, - MakeFunctionDecl( - "split", - MakeMemberOverloadDecl("string_split_string", ListStringType(), - StringType(), StringType()), - MakeMemberOverloadDecl("string_split_string_int", ListStringType(), - StringType(), StringType(), IntType()))); - CEL_ASSIGN_OR_RETURN( - auto lower_decl, - MakeFunctionDecl("lowerAscii", - MakeMemberOverloadDecl("string_lower_ascii", - StringType(), StringType()))); - - CEL_ASSIGN_OR_RETURN( - auto replace_decl, - MakeFunctionDecl( - "replace", - MakeMemberOverloadDecl("string_replace_string_string", StringType(), - StringType(), StringType(), StringType()), - MakeMemberOverloadDecl("string_replace_string_string_int", - StringType(), StringType(), StringType(), - StringType(), IntType()))); - - // Additional functions described in the spec. - CEL_ASSIGN_OR_RETURN( - auto char_at_decl, - MakeFunctionDecl( - "charAt", MakeMemberOverloadDecl("string_char_at_int", StringType(), - StringType(), IntType()))); - CEL_ASSIGN_OR_RETURN( - auto index_of_decl, - MakeFunctionDecl( - "indexOf", - MakeMemberOverloadDecl("string_index_of_string", IntType(), - StringType(), StringType()), - MakeMemberOverloadDecl("string_index_of_string_int", IntType(), - StringType(), StringType(), IntType()))); - CEL_ASSIGN_OR_RETURN( - auto last_index_of_decl, - MakeFunctionDecl( - "lastIndexOf", - MakeMemberOverloadDecl("string_last_index_of_string", IntType(), - StringType(), StringType()), - MakeMemberOverloadDecl("string_last_index_of_string_int", IntType(), - StringType(), StringType(), IntType()))); - - CEL_ASSIGN_OR_RETURN( - auto substring_decl, - MakeFunctionDecl( - "substring", - MakeMemberOverloadDecl("string_substring_int", StringType(), - StringType(), IntType()), - MakeMemberOverloadDecl("string_substring_int_int", StringType(), - StringType(), IntType(), IntType()))); - CEL_ASSIGN_OR_RETURN( - auto upper_ascii_decl, - MakeFunctionDecl("upperAscii", - MakeMemberOverloadDecl("string_upper_ascii", - StringType(), StringType()))); - CEL_ASSIGN_OR_RETURN( - auto format_decl, - MakeFunctionDecl("format", - MakeMemberOverloadDecl("string_format", StringType(), - StringType(), ListType()))); - CEL_ASSIGN_OR_RETURN( - auto quote_decl, - MakeFunctionDecl( - "strings.quote", - MakeOverloadDecl("strings_quote", StringType(), StringType()))); - - CEL_ASSIGN_OR_RETURN( - auto reverse_decl, - MakeFunctionDecl("reverse", - MakeMemberOverloadDecl("string_reverse", StringType(), - StringType()))); - - CEL_ASSIGN_OR_RETURN( - auto trim_decl, - MakeFunctionDecl("trim", MakeMemberOverloadDecl( - "string_trim", StringType(), StringType()))); - - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(split_decl))); - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(lower_decl))); - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(replace_decl))); - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(char_at_decl))); - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(index_of_decl))); - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(last_index_of_decl))); - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(substring_decl))); - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(upper_ascii_decl))); - CEL_RETURN_IF_ERROR(builder.MergeFunction(std::move(trim_decl))); - if (version == 0) { - return absl::OkStatus(); - } - - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(format_decl))); - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(quote_decl))); - if (version == 1) { - return absl::OkStatus(); - } - - CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(join_decl))); - if (version == 2) { - return absl::OkStatus(); - } - - // MergeFunction is used to combine with the reverse function - // defined in cel.lib.ext.lists extension. - CEL_RETURN_IF_ERROR(builder.MergeFunction(std::move(reverse_decl))); - - return absl::OkStatus(); -} - -} // namespace - -absl::Status RegisterStringsFunctions( - FunctionRegistry& registry, const RuntimeOptions& options, - const StringsExtensionOptions& extension_options) { - const int version = extension_options.version; - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter, StringValue, StringValue>:: - CreateDescriptor("split", /*receiver_style=*/true), - BinaryFunctionAdapter, StringValue, - StringValue>::WrapFunction(Split2))); - CEL_RETURN_IF_ERROR(registry.Register( - TernaryFunctionAdapter< - absl::StatusOr, StringValue, StringValue, - int64_t>::CreateDescriptor("split", /*receiver_style=*/true), - TernaryFunctionAdapter, StringValue, StringValue, - int64_t>::WrapFunction(Split3))); - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter, StringValue>:: - CreateDescriptor("lowerAscii", /*receiver_style=*/true), - UnaryFunctionAdapter, StringValue>::WrapFunction( - LowerAscii))); - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter, StringValue>:: - CreateDescriptor("upperAscii", /*receiver_style=*/true), - UnaryFunctionAdapter, StringValue>::WrapFunction( - UpperAscii))); - CEL_RETURN_IF_ERROR(registry.Register( - TernaryFunctionAdapter< - absl::StatusOr, StringValue, StringValue, - StringValue>::CreateDescriptor("replace", /*receiver_style=*/true), - TernaryFunctionAdapter, StringValue, StringValue, - StringValue>::WrapFunction(Replace1))); - CEL_RETURN_IF_ERROR(registry.Register( - QuaternaryFunctionAdapter< - absl::StatusOr, StringValue, StringValue, StringValue, - int64_t>::CreateDescriptor("replace", /*receiver_style=*/true), - QuaternaryFunctionAdapter, StringValue, StringValue, - StringValue, int64_t>::WrapFunction(Replace2))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterMemberOverload("charAt", &CharAt, - registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterMemberOverload("indexOf", - &IndexOf2, - registry))); - CEL_RETURN_IF_ERROR( - (TernaryFunctionAdapter::RegisterMemberOverload("indexOf", - &IndexOf3, - registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterMemberOverload("lastIndexOf", - &LastIndexOf2, - registry))); - CEL_RETURN_IF_ERROR( - (TernaryFunctionAdapter::RegisterMemberOverload("lastIndexOf", - &LastIndexOf3, - registry))); - CEL_RETURN_IF_ERROR( - (BinaryFunctionAdapter::RegisterMemberOverload("substring", - &Substring2, - registry))); - CEL_RETURN_IF_ERROR( - (TernaryFunctionAdapter::RegisterMemberOverload("substring", - &Substring3, - registry))); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterMemberOverload( - "trim", &Trim, registry))); - if (version == 0) { - return absl::OkStatus(); - } - - CEL_RETURN_IF_ERROR(RegisterStringFormattingFunctions( - registry, options, {extension_options.max_precision})); - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - "strings.quote", &Quote, registry))); - if (version == 1) { - return absl::OkStatus(); - } - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter, ListValue>::CreateDescriptor( - "join", /*receiver_style=*/true), - UnaryFunctionAdapter, ListValue>::WrapFunction( - Join1))); - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter, ListValue, StringValue>:: - CreateDescriptor("join", /*receiver_style=*/true), - BinaryFunctionAdapter, ListValue, - StringValue>::WrapFunction(Join2))); - if (version == 2) { - return absl::OkStatus(); - } - - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterMemberOverload( - "reverse", &Reverse, registry))); - return absl::OkStatus(); -} - absl::Status RegisterStringsFunctions( google::api::expr::runtime::CelFunctionRegistry* registry, const google::api::expr::runtime::InterpreterOptions& options, @@ -428,11 +30,4 @@ absl::Status RegisterStringsFunctions( extension_options); } -CheckerLibrary StringsCheckerLibrary(const StringsExtensionOptions& options) { - const int version = options.version; - return {"strings", [version](TypeCheckerBuilder& builder) { - return RegisterStringsDecls(builder, version); - }}; -} - } // namespace cel::extensions diff --git a/extensions/strings.h b/extensions/strings.h index 3ec92d603..a3f1132f3 100644 --- a/extensions/strings.h +++ b/extensions/strings.h @@ -16,58 +16,17 @@ #define THIRD_PARTY_CEL_CPP_EXTENSIONS_STRINGS_H_ #include "absl/status/status.h" -#include "checker/type_checker_builder.h" -#include "compiler/compiler.h" #include "eval/public/cel_function_registry.h" #include "eval/public/cel_options.h" -#include "runtime/function_registry.h" -#include "runtime/runtime_options.h" +#include "extensions/strings_modern.h" // IWYU pragma: export namespace cel::extensions { -constexpr int kStringsExtensionLatestVersion = 4; - -struct StringsExtensionOptions { - int version = kStringsExtensionLatestVersion; - - // Maximum precision allowed for floating point format specifiers in - // format() function. This is used for both fixed and scientific notations. - // Value must be in the range [0, 1000], otherwise clamped. - // - // Does not affect default precisions for %e and %f format specifiers. - int max_precision = 1000; -}; - -// Register extension functions for strings. -absl::Status RegisterStringsFunctions( - FunctionRegistry& registry, const RuntimeOptions& options, - const StringsExtensionOptions& extension_options = {}); - absl::Status RegisterStringsFunctions( google::api::expr::runtime::CelFunctionRegistry* registry, const google::api::expr::runtime::InterpreterOptions& options, const StringsExtensionOptions& extension_options = {}); -CheckerLibrary StringsCheckerLibrary( - const StringsExtensionOptions& extension_options = {}); - -inline CheckerLibrary StringsCheckerLibrary(int version) { - StringsExtensionOptions options; - options.version = version; - return StringsCheckerLibrary(options); -} - -inline CompilerLibrary StringsCompilerLibrary( - const StringsExtensionOptions& options = {}) { - return CompilerLibrary::FromCheckerLibrary(StringsCheckerLibrary(options)); -} - -inline CompilerLibrary StringsCompilerLibrary(int version) { - StringsExtensionOptions options; - options.version = version; - return StringsCompilerLibrary(options); -} - } // namespace cel::extensions #endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_STRINGS_H_ diff --git a/extensions/strings_modern.cc b/extensions/strings_modern.cc new file mode 100644 index 000000000..cdfeac8dc --- /dev/null +++ b/extensions/strings_modern.cc @@ -0,0 +1,415 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "extensions/strings_modern.h" + +#include +#include +#include + +#include "absl/base/no_destructor.h" +#include "absl/base/nullability.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "checker/internal/builtins_arena.h" +#include "checker/type_checker_builder.h" +#include "common/decl.h" +#include "common/type.h" +#include "common/value.h" +#include "extensions/formatting.h" +#include "internal/status_macros.h" +#include "runtime/function.h" +#include "runtime/function_adapter.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" + +namespace cel::extensions { + +namespace { + +using ::cel::checker_internal::BuiltinsArena; + +absl::StatusOr Join2( + const ListValue& value, const StringValue& separator, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + return separator.Join(value, descriptor_pool, message_factory, arena); +} + +absl::StatusOr Join1( + const ListValue& value, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + return StringValue().Join(value, descriptor_pool, message_factory, arena); +} + +absl::StatusOr Split3( + const StringValue& string, const StringValue& delimiter, int64_t limit, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + return string.Split(delimiter, limit, arena); +} + +absl::StatusOr Split2( + const StringValue& string, const StringValue& delimiter, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + return string.Split(delimiter, arena); +} + +absl::StatusOr Replace2(const StringValue& string, + const StringValue& old_sub, + const StringValue& new_sub, int64_t limit, + const google::protobuf::DescriptorPool* absl_nonnull, + google::protobuf::MessageFactory* absl_nonnull, + google::protobuf::Arena* absl_nonnull arena) { + return string.Replace(old_sub, new_sub, limit, arena); +} + +absl::StatusOr Replace1( + const StringValue& string, const StringValue& old_sub, + const StringValue& new_sub, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + return string.Replace(old_sub, new_sub, -1, arena); +} + +Value CharAt(const StringValue& string, int64_t pos, + const Function::InvokeContext& context) { + return string.CharAt(pos, context.arena()); +} + +int64_t IndexOf2(const StringValue& haystack, const StringValue& needle) { + return haystack.IndexOf(needle).value_or(-1); +} + +Value IndexOf3(const StringValue& haystack, const StringValue& needle, + int64_t pos, const Function::InvokeContext& context) { + if (pos > haystack.Size()) { + return ErrorValue::From( + absl::InvalidArgumentError(absl::StrCat("index out of range: ", pos)), + context.arena()); + } + return IntValue(haystack.IndexOf(needle, pos).value_or(-1)); +} + +int64_t LastIndexOf2(const StringValue& haystack, const StringValue& needle) { + return haystack.LastIndexOf(needle).value_or(-1); +} + +Value LastIndexOf3(const StringValue& haystack, const StringValue& needle, + int64_t pos, const Function::InvokeContext& context) { + if (pos < 0 || pos > haystack.Size()) { + return ErrorValue::From( + absl::InvalidArgumentError(absl::StrCat("index out of range: ", pos)), + context.arena()); + } + return IntValue(haystack.LastIndexOf(needle, pos).value_or(-1)); +} + +Value Substring2(const StringValue& string, int64_t start, + const Function::InvokeContext& context) { + return string.Substring(start, context.arena()); +} + +Value Substring3(const StringValue& string, int64_t start, int64_t end, + const Function::InvokeContext& context) { + return string.Substring(start, end, context.arena()); +} + +StringValue Trim(const StringValue& string) { return string.Trim(); } + +StringValue LowerAscii(const StringValue& string, + const google::protobuf::DescriptorPool* absl_nonnull, + google::protobuf::MessageFactory* absl_nonnull, + google::protobuf::Arena* absl_nonnull arena) { + return string.LowerAscii(arena); +} + +StringValue UpperAscii(const StringValue& string, + const google::protobuf::DescriptorPool* absl_nonnull, + google::protobuf::MessageFactory* absl_nonnull, + google::protobuf::Arena* absl_nonnull arena) { + return string.UpperAscii(arena); +} + +StringValue Quote(const StringValue& string, + const google::protobuf::DescriptorPool* absl_nonnull, + google::protobuf::MessageFactory* absl_nonnull, + google::protobuf::Arena* absl_nonnull arena) { + return string.Quote(arena); +} + +StringValue Reverse(const StringValue& string, + const google::protobuf::DescriptorPool* absl_nonnull, + google::protobuf::MessageFactory* absl_nonnull, + google::protobuf::Arena* absl_nonnull arena) { + return string.Reverse(arena); +} + +const Type& ListStringType() { + static absl::NoDestructor kInstance( + ListType(BuiltinsArena(), StringType())); + return *kInstance; +} + +absl::Status RegisterStringsDecls(TypeCheckerBuilder& builder, int version) { + // Runtime Supported functions. + CEL_ASSIGN_OR_RETURN( + auto join_decl, + MakeFunctionDecl( + "join", + MakeMemberOverloadDecl("list_join", StringType(), ListStringType()), + MakeMemberOverloadDecl("list_join_string", StringType(), + ListStringType(), StringType()))); + CEL_ASSIGN_OR_RETURN( + auto split_decl, + MakeFunctionDecl( + "split", + MakeMemberOverloadDecl("string_split_string", ListStringType(), + StringType(), StringType()), + MakeMemberOverloadDecl("string_split_string_int", ListStringType(), + StringType(), StringType(), IntType()))); + CEL_ASSIGN_OR_RETURN( + auto lower_decl, + MakeFunctionDecl("lowerAscii", + MakeMemberOverloadDecl("string_lower_ascii", + StringType(), StringType()))); + + CEL_ASSIGN_OR_RETURN( + auto replace_decl, + MakeFunctionDecl( + "replace", + MakeMemberOverloadDecl("string_replace_string_string", StringType(), + StringType(), StringType(), StringType()), + MakeMemberOverloadDecl("string_replace_string_string_int", + StringType(), StringType(), StringType(), + StringType(), IntType()))); + + // Additional functions described in the spec. + CEL_ASSIGN_OR_RETURN( + auto char_at_decl, + MakeFunctionDecl( + "charAt", MakeMemberOverloadDecl("string_char_at_int", StringType(), + StringType(), IntType()))); + CEL_ASSIGN_OR_RETURN( + auto index_of_decl, + MakeFunctionDecl( + "indexOf", + MakeMemberOverloadDecl("string_index_of_string", IntType(), + StringType(), StringType()), + MakeMemberOverloadDecl("string_index_of_string_int", IntType(), + StringType(), StringType(), IntType()))); + CEL_ASSIGN_OR_RETURN( + auto last_index_of_decl, + MakeFunctionDecl( + "lastIndexOf", + MakeMemberOverloadDecl("string_last_index_of_string", IntType(), + StringType(), StringType()), + MakeMemberOverloadDecl("string_last_index_of_string_int", IntType(), + StringType(), StringType(), IntType()))); + + CEL_ASSIGN_OR_RETURN( + auto substring_decl, + MakeFunctionDecl( + "substring", + MakeMemberOverloadDecl("string_substring_int", StringType(), + StringType(), IntType()), + MakeMemberOverloadDecl("string_substring_int_int", StringType(), + StringType(), IntType(), IntType()))); + CEL_ASSIGN_OR_RETURN( + auto upper_ascii_decl, + MakeFunctionDecl("upperAscii", + MakeMemberOverloadDecl("string_upper_ascii", + StringType(), StringType()))); + CEL_ASSIGN_OR_RETURN( + auto format_decl, + MakeFunctionDecl("format", + MakeMemberOverloadDecl("string_format", StringType(), + StringType(), ListType()))); + CEL_ASSIGN_OR_RETURN( + auto quote_decl, + MakeFunctionDecl( + "strings.quote", + MakeOverloadDecl("strings_quote", StringType(), StringType()))); + + CEL_ASSIGN_OR_RETURN( + auto reverse_decl, + MakeFunctionDecl("reverse", + MakeMemberOverloadDecl("string_reverse", StringType(), + StringType()))); + + CEL_ASSIGN_OR_RETURN( + auto trim_decl, + MakeFunctionDecl("trim", MakeMemberOverloadDecl( + "string_trim", StringType(), StringType()))); + + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(split_decl))); + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(lower_decl))); + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(replace_decl))); + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(char_at_decl))); + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(index_of_decl))); + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(last_index_of_decl))); + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(substring_decl))); + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(upper_ascii_decl))); + CEL_RETURN_IF_ERROR(builder.MergeFunction(std::move(trim_decl))); + if (version == 0) { + return absl::OkStatus(); + } + + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(format_decl))); + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(quote_decl))); + if (version == 1) { + return absl::OkStatus(); + } + + CEL_RETURN_IF_ERROR(builder.AddFunction(std::move(join_decl))); + if (version == 2) { + return absl::OkStatus(); + } + + // MergeFunction is used to combine with the reverse function + // defined in cel.lib.ext.lists extension. + CEL_RETURN_IF_ERROR(builder.MergeFunction(std::move(reverse_decl))); + + return absl::OkStatus(); +} + +} // namespace + +absl::Status RegisterStringsFunctions( + FunctionRegistry& registry, const RuntimeOptions& options, + const StringsExtensionOptions& extension_options) { + const int version = extension_options.version; + CEL_RETURN_IF_ERROR(registry.Register( + BinaryFunctionAdapter, StringValue, StringValue>:: + CreateDescriptor("split", /*receiver_style=*/true), + BinaryFunctionAdapter, StringValue, + StringValue>::WrapFunction(Split2))); + CEL_RETURN_IF_ERROR(registry.Register( + TernaryFunctionAdapter< + absl::StatusOr, StringValue, StringValue, + int64_t>::CreateDescriptor("split", /*receiver_style=*/true), + TernaryFunctionAdapter, StringValue, StringValue, + int64_t>::WrapFunction(Split3))); + CEL_RETURN_IF_ERROR(registry.Register( + UnaryFunctionAdapter, StringValue>:: + CreateDescriptor("lowerAscii", /*receiver_style=*/true), + UnaryFunctionAdapter, StringValue>::WrapFunction( + LowerAscii))); + CEL_RETURN_IF_ERROR(registry.Register( + UnaryFunctionAdapter, StringValue>:: + CreateDescriptor("upperAscii", /*receiver_style=*/true), + UnaryFunctionAdapter, StringValue>::WrapFunction( + UpperAscii))); + CEL_RETURN_IF_ERROR(registry.Register( + TernaryFunctionAdapter< + absl::StatusOr, StringValue, StringValue, + StringValue>::CreateDescriptor("replace", /*receiver_style=*/true), + TernaryFunctionAdapter, StringValue, StringValue, + StringValue>::WrapFunction(Replace1))); + CEL_RETURN_IF_ERROR(registry.Register( + QuaternaryFunctionAdapter< + absl::StatusOr, StringValue, StringValue, StringValue, + int64_t>::CreateDescriptor("replace", /*receiver_style=*/true), + QuaternaryFunctionAdapter, StringValue, StringValue, + StringValue, int64_t>::WrapFunction(Replace2))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterMemberOverload("charAt", &CharAt, + registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterMemberOverload("indexOf", + &IndexOf2, + registry))); + CEL_RETURN_IF_ERROR( + (TernaryFunctionAdapter::RegisterMemberOverload("indexOf", + &IndexOf3, + registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterMemberOverload("lastIndexOf", + &LastIndexOf2, + registry))); + CEL_RETURN_IF_ERROR( + (TernaryFunctionAdapter::RegisterMemberOverload("lastIndexOf", + &LastIndexOf3, + registry))); + CEL_RETURN_IF_ERROR( + (BinaryFunctionAdapter::RegisterMemberOverload("substring", + &Substring2, + registry))); + CEL_RETURN_IF_ERROR( + (TernaryFunctionAdapter::RegisterMemberOverload("substring", + &Substring3, + registry))); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterMemberOverload( + "trim", &Trim, registry))); + if (version == 0) { + return absl::OkStatus(); + } + + CEL_RETURN_IF_ERROR(RegisterStringFormattingFunctions( + registry, options, {extension_options.max_precision})); + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterGlobalOverload( + "strings.quote", &Quote, registry))); + if (version == 1) { + return absl::OkStatus(); + } + + CEL_RETURN_IF_ERROR(registry.Register( + UnaryFunctionAdapter, ListValue>::CreateDescriptor( + "join", /*receiver_style=*/true), + UnaryFunctionAdapter, ListValue>::WrapFunction( + Join1))); + CEL_RETURN_IF_ERROR(registry.Register( + BinaryFunctionAdapter, ListValue, StringValue>:: + CreateDescriptor("join", /*receiver_style=*/true), + BinaryFunctionAdapter, ListValue, + StringValue>::WrapFunction(Join2))); + if (version == 2) { + return absl::OkStatus(); + } + + CEL_RETURN_IF_ERROR( + (UnaryFunctionAdapter::RegisterMemberOverload( + "reverse", &Reverse, registry))); + return absl::OkStatus(); +} + +CheckerLibrary StringsCheckerLibrary(const StringsExtensionOptions& options) { + const int version = options.version; + return {"strings", [version](TypeCheckerBuilder& builder) { + return RegisterStringsDecls(builder, version); + }}; +} + +} // namespace cel::extensions diff --git a/extensions/strings_modern.h b/extensions/strings_modern.h new file mode 100644 index 000000000..45087c5fd --- /dev/null +++ b/extensions/strings_modern.h @@ -0,0 +1,66 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_STRINGS_MODERN_H_ +#define THIRD_PARTY_CEL_CPP_EXTENSIONS_STRINGS_MODERN_H_ + +#include "absl/status/status.h" +#include "checker/type_checker_builder.h" +#include "compiler/compiler.h" +#include "runtime/function_registry.h" +#include "runtime/runtime_options.h" + +namespace cel::extensions { + +inline constexpr int kStringsExtensionLatestVersion = 4; + +struct StringsExtensionOptions { + int version = kStringsExtensionLatestVersion; + + // Maximum precision allowed for floating point format specifiers in + // format() function. This is used for both fixed and scientific notations. + // Value must be in the range [0, 1000], otherwise clamped. + // + // Does not affect default precisions for %e and %f format specifiers. + int max_precision = 1000; +}; + +// Register extension functions for strings. +absl::Status RegisterStringsFunctions( + FunctionRegistry& registry, const RuntimeOptions& options, + const StringsExtensionOptions& extension_options = {}); + +CheckerLibrary StringsCheckerLibrary( + const StringsExtensionOptions& extension_options = {}); + +inline CheckerLibrary StringsCheckerLibrary(int version) { + StringsExtensionOptions options; + options.version = version; + return StringsCheckerLibrary(options); +} + +inline CompilerLibrary StringsCompilerLibrary( + const StringsExtensionOptions& options = {}) { + return CompilerLibrary::FromCheckerLibrary(StringsCheckerLibrary(options)); +} + +inline CompilerLibrary StringsCompilerLibrary(int version) { + StringsExtensionOptions options; + options.version = version; + return StringsCompilerLibrary(options); +} + +} // namespace cel::extensions + +#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_STRINGS_MODERN_H_ diff --git a/runtime/BUILD b/runtime/BUILD index f4be5adbf..754a6622b 100644 --- a/runtime/BUILD +++ b/runtime/BUILD @@ -212,7 +212,6 @@ cc_library( "//base:data", "//common:type", "//common:value", - "//runtime/internal:legacy_runtime_type_provider", "//runtime/internal:runtime_type_provider", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/base:nullability", diff --git a/runtime/internal/BUILD b/runtime/internal/BUILD index 0380de236..605a7bbc6 100644 --- a/runtime/internal/BUILD +++ b/runtime/internal/BUILD @@ -37,8 +37,6 @@ cc_library( srcs = ["runtime_env.cc"], hdrs = ["runtime_env.h"], deps = [ - "//eval/public:cel_function_registry", - "//eval/public:cel_type_registry", "//internal:noop_delete", "//internal:well_known_types", "//runtime:function_registry", @@ -51,6 +49,38 @@ cc_library( ], ) +cc_library( + name = "legacy_runtime_env", + srcs = ["legacy_runtime_env.cc"], + hdrs = ["legacy_runtime_env.h"], + deps = [ + ":runtime_env", + "//eval/public:cel_function_registry", + "//eval/public:cel_type_registry", + "//internal:status_macros", + "@com_google_absl//absl/base:nullability", + "@com_google_absl//absl/status:statusor", + "@com_google_protobuf//:protobuf", + ], +) + +cc_library( + name = "legacy_runtime_env_testing", + testonly = True, + srcs = ["legacy_runtime_env_testing.cc"], + hdrs = ["legacy_runtime_env_testing.h"], + deps = [ + ":legacy_runtime_env", + "//internal:noop_delete", + "//internal:testing_descriptor_pool", + "//internal:testing_message_factory", + "@com_google_absl//absl/base:nullability", + "@com_google_absl//absl/log:absl_check", + "@com_google_absl//absl/status:statusor", + "@com_google_protobuf//:protobuf", + ], +) + cc_library( name = "runtime_impl", srcs = ["runtime_impl.cc"], diff --git a/runtime/internal/legacy_runtime_env.cc b/runtime/internal/legacy_runtime_env.cc new file mode 100644 index 000000000..8f98b8e45 --- /dev/null +++ b/runtime/internal/legacy_runtime_env.cc @@ -0,0 +1,47 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "runtime/internal/legacy_runtime_env.h" + +#include +#include + +#include "absl/base/nullability.h" +#include "absl/status/statusor.h" +#include "internal/status_macros.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" + +namespace cel::runtime_internal { + +absl::StatusOr> +LegacyRuntimeEnv::Create( + absl_nonnull std::shared_ptr descriptor_pool, + absl_nullable std::shared_ptr message_factory) { + auto env = std::make_shared( + PrivateTag{}, std::move(descriptor_pool), std::move(message_factory)); + CEL_RETURN_IF_ERROR(env->runtime_env.Initialize()); + return env; +} + +LegacyRuntimeEnv::LegacyRuntimeEnv( + PrivateTag, + absl_nonnull std::shared_ptr descriptor_pool, + absl_nullable std::shared_ptr message_factory) + : legacy_type_registry(descriptor_pool.get(), message_factory.get()), + runtime_env(std::move(descriptor_pool), std::move(message_factory), + legacy_type_registry.InternalGetModernRegistry(), + legacy_function_registry.InternalGetRegistry()) {} + +} // namespace cel::runtime_internal diff --git a/runtime/internal/legacy_runtime_env.h b/runtime/internal/legacy_runtime_env.h new file mode 100644 index 000000000..8e2f6692f --- /dev/null +++ b/runtime/internal/legacy_runtime_env.h @@ -0,0 +1,78 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_CEL_CPP_RUNTIME_INTERNAL_LEGACY_RUNTIME_ENV_H_ +#define THIRD_PARTY_CEL_CPP_RUNTIME_INTERNAL_LEGACY_RUNTIME_ENV_H_ + +#include + +#include "absl/base/nullability.h" +#include "absl/status/statusor.h" +#include "eval/public/cel_function_registry.h" +#include "eval/public/cel_type_registry.h" +#include "runtime/internal/runtime_env.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" + +namespace cel::runtime_internal { + +// Legacy runtime environment owning `CelTypeRegistry` and +// `CelFunctionRegistry` alongside the modern `RuntimeEnv` whose references +// alias into them. +// +// Always allocate via `LegacyRuntimeEnv::Create(...)`. `GetRuntimeEnv()` +// returns an aliasing `std::shared_ptr` that shares ownership of +// this `LegacyRuntimeEnv` so the legacy registries strictly outlive any +// `RuntimeEnv` / `CelExpressionFlatImpl` references. +struct LegacyRuntimeEnv final + : public std::enable_shared_from_this { + private: + struct PrivateTag {}; + + public: + static absl::StatusOr> Create( + absl_nonnull std::shared_ptr + descriptor_pool, + absl_nullable std::shared_ptr message_factory = + nullptr); + + LegacyRuntimeEnv( + PrivateTag, + absl_nonnull std::shared_ptr + descriptor_pool, + absl_nullable std::shared_ptr message_factory); + + LegacyRuntimeEnv(const LegacyRuntimeEnv&) = delete; + LegacyRuntimeEnv(LegacyRuntimeEnv&&) = delete; + LegacyRuntimeEnv& operator=(const LegacyRuntimeEnv&) = delete; + LegacyRuntimeEnv& operator=(LegacyRuntimeEnv&&) = delete; + + absl_nonnull std::shared_ptr GetRuntimeEnv() { + return std::shared_ptr(shared_from_this(), &runtime_env); + } + + absl_nonnull std::shared_ptr GetRuntimeEnv() const { + return std::shared_ptr(shared_from_this(), &runtime_env); + } + + // Declared before `runtime_env` so `runtime_env` (and any `KeepAlive` entries + // referencing these registries) is destroyed before them. + google::api::expr::runtime::CelTypeRegistry legacy_type_registry; + google::api::expr::runtime::CelFunctionRegistry legacy_function_registry; + RuntimeEnv runtime_env; +}; + +} // namespace cel::runtime_internal + +#endif // THIRD_PARTY_CEL_CPP_RUNTIME_INTERNAL_LEGACY_RUNTIME_ENV_H_ diff --git a/runtime/internal/legacy_runtime_env_testing.cc b/runtime/internal/legacy_runtime_env_testing.cc new file mode 100644 index 000000000..b1e8ba001 --- /dev/null +++ b/runtime/internal/legacy_runtime_env_testing.cc @@ -0,0 +1,42 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "runtime/internal/legacy_runtime_env_testing.h" + +#include +#include + +#include "absl/base/nullability.h" +#include "absl/log/absl_check.h" +#include "absl/status/statusor.h" +#include "internal/noop_delete.h" +#include "internal/testing_descriptor_pool.h" +#include "internal/testing_message_factory.h" +#include "runtime/internal/legacy_runtime_env.h" +#include "google/protobuf/message.h" + +namespace cel::runtime_internal { + +absl_nonnull std::shared_ptr NewTestingLegacyRuntimeEnv() { + absl::StatusOr> env = + LegacyRuntimeEnv::Create( + internal::GetSharedTestingDescriptorPool(), + std::shared_ptr( + internal::GetTestingMessageFactory(), + internal::NoopDeleteFor())); + ABSL_CHECK_OK(env.status()); // Crash OK + return *std::move(env); +} + +} // namespace cel::runtime_internal diff --git a/runtime/internal/legacy_runtime_env_testing.h b/runtime/internal/legacy_runtime_env_testing.h new file mode 100644 index 000000000..838eda4b1 --- /dev/null +++ b/runtime/internal/legacy_runtime_env_testing.h @@ -0,0 +1,29 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_CEL_CPP_RUNTIME_INTERNAL_LEGACY_RUNTIME_ENV_TESTING_H_ +#define THIRD_PARTY_CEL_CPP_RUNTIME_INTERNAL_LEGACY_RUNTIME_ENV_TESTING_H_ + +#include + +#include "absl/base/nullability.h" +#include "runtime/internal/legacy_runtime_env.h" + +namespace cel::runtime_internal { + +absl_nonnull std::shared_ptr NewTestingLegacyRuntimeEnv(); + +} // namespace cel::runtime_internal + +#endif // THIRD_PARTY_CEL_CPP_RUNTIME_INTERNAL_LEGACY_RUNTIME_ENV_TESTING_H_ diff --git a/runtime/internal/runtime_env.h b/runtime/internal/runtime_env.h index efc68cd5f..462e04e72 100644 --- a/runtime/internal/runtime_env.h +++ b/runtime/internal/runtime_env.h @@ -25,8 +25,6 @@ #include "absl/base/thread_annotations.h" #include "absl/status/status.h" #include "absl/synchronization/mutex.h" -#include "eval/public/cel_function_registry.h" -#include "eval/public/cel_type_registry.h" #include "internal/well_known_types.h" #include "runtime/function_registry.h" #include "runtime/type_registry.h" @@ -46,10 +44,28 @@ struct RuntimeEnv final { message_factory = nullptr) : descriptor_pool(std::move(descriptor_pool)), message_factory(std::move(message_factory)), - legacy_type_registry(this->descriptor_pool.get(), - this->message_factory.get()), - type_registry(legacy_type_registry.InternalGetModernRegistry()), - function_registry(legacy_function_registry.InternalGetRegistry()) { + owned_type_registry_(std::make_unique( + this->descriptor_pool.get(), this->message_factory.get())), + owned_function_registry_(std::make_unique()), + type_registry(*owned_type_registry_), + function_registry(*owned_function_registry_) { + if (this->message_factory != nullptr) { + message_factory_ptr.store(this->message_factory.get(), + std::memory_order_seq_cst); + } + } + + // Binds `RuntimeEnv` to caller-owned `TypeRegistry` and `FunctionRegistry` + // instances, which must outlive this `RuntimeEnv` (and its `keep_alives`). + RuntimeEnv( + absl_nonnull std::shared_ptr + descriptor_pool, + absl_nullable std::shared_ptr message_factory, + TypeRegistry& type_registry, FunctionRegistry& function_registry) + : descriptor_pool(std::move(descriptor_pool)), + message_factory(std::move(message_factory)), + type_registry(type_registry), + function_registry(function_registry) { if (this->message_factory != nullptr) { message_factory_ptr.store(this->message_factory.get(), std::memory_order_seq_cst); @@ -104,14 +120,14 @@ struct RuntimeEnv final { std::deque> deque; }; + // Declared before `keep_alives` so `keep_alives` are destroyed before + // owned registries. + std::unique_ptr owned_type_registry_; + std::unique_ptr owned_function_registry_; + KeepAlives keep_alives; public: - // Because of legacy shenanigans, we use shared_ptr here. For legacy, this is - // an unowned shared_ptr (a noop deleter) pointing to the modern equivalent - // which is a member of the legacy variant. - google::api::expr::runtime::CelTypeRegistry legacy_type_registry; - google::api::expr::runtime::CelFunctionRegistry legacy_function_registry; TypeRegistry& type_registry; FunctionRegistry& function_registry; diff --git a/runtime/standard/BUILD b/runtime/standard/BUILD index d08ac2116..f78c02f57 100644 --- a/runtime/standard/BUILD +++ b/runtime/standard/BUILD @@ -33,14 +33,20 @@ cc_library( ], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", + "//common:value_kind", "//internal:number", "//internal:status_macros", + "//runtime:function", "//runtime:function_registry", "//runtime:runtime_options", "@com_google_absl//absl/status", - "@com_google_absl//absl/time", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", ], ) @@ -69,18 +75,19 @@ cc_library( ], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", + "//common:value_kind", "//internal:number", "//internal:status_macros", + "//runtime:function", "//runtime:function_registry", - "//runtime:register_function_helper", "//runtime:runtime_options", - "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_protobuf//:protobuf", + "@com_google_absl//absl/types:span", ], ) @@ -108,22 +115,23 @@ cc_library( hdrs = ["equality_functions.h"], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", "//common:value_kind", "//internal:number", "//internal:status_macros", + "//runtime:function", "//runtime:function_registry", - "//runtime:register_function_helper", "//runtime:runtime_options", "//runtime/internal:errors", "@com_google_absl//absl/base:nullability", - "@com_google_absl//absl/functional:function_ref", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", "@com_google_protobuf//:protobuf", ], ) @@ -156,16 +164,17 @@ cc_library( ], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", "//internal:status_macros", "//runtime:function", "//runtime:function_registry", - "//runtime:register_function_helper", "//runtime:runtime_options", "//runtime/internal:errors", "@com_google_absl//absl/status", - "@com_google_absl//absl/strings", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/types:span", ], ) @@ -203,14 +212,18 @@ cc_library( hdrs = ["container_functions.h"], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", + "//common:value_kind", "//internal:status_macros", + "//runtime:function", "//runtime:function_registry", "//runtime:runtime_options", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/types:span", "@com_google_protobuf//:protobuf", ], ) @@ -235,8 +248,10 @@ cc_library( hdrs = ["type_conversion_functions.h"], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", + "//common:value_kind", "//internal:overflow", "//internal:status_macros", "//internal:time", @@ -247,8 +262,8 @@ cc_library( "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", + "@com_google_absl//absl/types:span", "@com_google_protobuf//:protobuf", ], ) @@ -273,15 +288,19 @@ cc_library( hdrs = ["arithmetic_functions.h"], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", + "//common:value_kind", "//internal:overflow", "//internal:status_macros", "//runtime:function", "//runtime:function_registry", "//runtime:runtime_options", "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", ], ) @@ -305,8 +324,10 @@ cc_library( hdrs = ["time_functions.h"], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", + "//common:value_kind", "//internal:overflow", "//internal:status_macros", "//runtime:function", @@ -316,7 +337,7 @@ cc_library( "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", - "@com_google_protobuf//:protobuf", + "@com_google_absl//absl/types:span", ], ) @@ -340,16 +361,18 @@ cc_library( hdrs = ["string_functions.h"], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", + "//common:value_kind", "//internal:status_macros", + "//runtime:function", "//runtime:function_registry", "//runtime:runtime_options", - "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_protobuf//:protobuf", + "@com_google_absl//absl/types:span", ], ) @@ -373,7 +396,8 @@ cc_library( hdrs = ["regex_functions.h"], deps = [ "//base:builtins", - "//base:function_adapter", + "//common:function_descriptor", + "//common:kind", "//common:value", "//internal:re2_options", "//internal:status_macros", @@ -381,7 +405,8 @@ cc_library( "//runtime:function_registry", "//runtime:runtime_options", "@com_google_absl//absl/status", - "@com_google_absl//absl/strings", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/types:span", "@com_googlesource_code_re2//:re2", ], ) diff --git a/runtime/standard/arithmetic_functions.cc b/runtime/standard/arithmetic_functions.cc index c77dd1a12..e44a31e3f 100644 --- a/runtime/standard/arithmetic_functions.cc +++ b/runtime/standard/arithmetic_functions.cc @@ -16,12 +16,17 @@ #include #include +#include #include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" +#include "common/value_kind.h" #include "internal/overflow.h" #include "internal/status_macros.h" #include "runtime/function.h" @@ -31,214 +36,174 @@ namespace cel { namespace { -// Template functions providing arithmetic operations -template -Value Add(Type v0, Type v1, const Function::InvokeContext& context); - -template <> -Value Add(int64_t v0, int64_t v1, - const Function::InvokeContext& context) { - auto sum = cel::internal::CheckedAdd(v0, v1); - if (!sum.ok()) { - return ErrorValue::From(sum.status(), context.arena()); - } - return IntValue(*sum); -} - -template <> -Value Add(uint64_t v0, uint64_t v1, - const Function::InvokeContext& context) { - auto sum = cel::internal::CheckedAdd(v0, v1); - if (!sum.ok()) { - return ErrorValue::From(sum.status(), context.arena()); - } - return UintValue(*sum); -} - -template <> -Value Add(double v0, double v1, const Function::InvokeContext&) { - return DoubleValue(v0 + v1); -} - -template -Value Sub(Type v0, Type v1, const Function::InvokeContext& context); - -template <> -Value Sub(int64_t v0, int64_t v1, - const Function::InvokeContext& context) { - auto diff = cel::internal::CheckedSub(v0, v1); - if (!diff.ok()) { - return ErrorValue::From(diff.status(), context.arena()); - } - return IntValue(*diff); -} - -template <> -Value Sub(uint64_t v0, uint64_t v1, - const Function::InvokeContext& context) { - auto diff = cel::internal::CheckedSub(v0, v1); - if (!diff.ok()) { - return ErrorValue::From(diff.status(), context.arena()); - } - return UintValue(*diff); -} - -template <> -Value Sub(double v0, double v1, const Function::InvokeContext&) { - return DoubleValue(v0 - v1); -} - -template -Value Mul(Type v0, Type v1, const Function::InvokeContext& context); - -template <> -Value Mul(int64_t v0, int64_t v1, - const Function::InvokeContext& context) { - auto prod = cel::internal::CheckedMul(v0, v1); - if (!prod.ok()) { - return ErrorValue::From(prod.status(), context.arena()); - } - return IntValue(*prod); -} - -template <> -Value Mul(uint64_t v0, uint64_t v1, - const Function::InvokeContext& context) { - auto prod = cel::internal::CheckedMul(v0, v1); - if (!prod.ok()) { - return ErrorValue::From(prod.status(), context.arena()); - } - return UintValue(*prod); -} - -template <> -Value Mul(double v0, double v1, const Function::InvokeContext&) { - return DoubleValue(v0 * v1); -} - -template -Value Div(Type v0, Type v1, const Function::InvokeContext& context); - -// Division operations for integer types should check for -// division by 0 -template <> -Value Div(int64_t v0, int64_t v1, - const Function::InvokeContext& context) { - auto quot = cel::internal::CheckedDiv(v0, v1); - if (!quot.ok()) { - return ErrorValue::From(quot.status(), context.arena()); +class ArithmeticBinaryFunction final : public Function { + public: + enum class Op { kAdd, kSubtract, kMultiply, kDivide, kModulo }; + + explicit ArithmeticBinaryFunction(Op op) : op_(op) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); + } + if (args[0].kind() != args[1].kind()) { + return absl::InvalidArgumentError( + "unexpected argument kinds for arithmetic function"); + } + switch (args[0].kind()) { + case ValueKind::kInt: { + int64_t lhs = args[0].GetInt().NativeValue(); + int64_t rhs = args[1].GetInt().NativeValue(); + absl::StatusOr result; + switch (op_) { + case Op::kAdd: + result = cel::internal::CheckedAdd(lhs, rhs); + break; + case Op::kSubtract: + result = cel::internal::CheckedSub(lhs, rhs); + break; + case Op::kMultiply: + result = cel::internal::CheckedMul(lhs, rhs); + break; + case Op::kDivide: + result = cel::internal::CheckedDiv(lhs, rhs); + break; + case Op::kModulo: + result = cel::internal::CheckedMod(lhs, rhs); + break; + } + if (!result.ok()) { + return ErrorValue::From(result.status(), context.arena()); + } + return IntValue(*result); + } + case ValueKind::kUint: { + uint64_t lhs = args[0].GetUint().NativeValue(); + uint64_t rhs = args[1].GetUint().NativeValue(); + absl::StatusOr result; + switch (op_) { + case Op::kAdd: + result = cel::internal::CheckedAdd(lhs, rhs); + break; + case Op::kSubtract: + result = cel::internal::CheckedSub(lhs, rhs); + break; + case Op::kMultiply: + result = cel::internal::CheckedMul(lhs, rhs); + break; + case Op::kDivide: + result = cel::internal::CheckedDiv(lhs, rhs); + break; + case Op::kModulo: + result = cel::internal::CheckedMod(lhs, rhs); + break; + } + if (!result.ok()) { + return ErrorValue::From(result.status(), context.arena()); + } + return UintValue(*result); + } + case ValueKind::kDouble: { + static_assert(std::numeric_limits::is_iec559, + "Division by zero for doubles must be supported"); + double lhs = args[0].GetDouble().NativeValue(); + double rhs = args[1].GetDouble().NativeValue(); + switch (op_) { + case Op::kAdd: + return DoubleValue(lhs + rhs); + case Op::kSubtract: + return DoubleValue(lhs - rhs); + case Op::kMultiply: + return DoubleValue(lhs * rhs); + case Op::kDivide: + // For double, division will result in +/- inf + return DoubleValue(lhs / rhs); + case Op::kModulo: + return absl::InvalidArgumentError( + "modulo is not defined for double"); + } + } + default: + return absl::InvalidArgumentError("expected numeric value"); + } } - return IntValue(*quot); -} -// Division operations for integer types should check for -// division by 0 -template <> -Value Div(uint64_t v0, uint64_t v1, - const Function::InvokeContext& context) { - auto quot = cel::internal::CheckedDiv(v0, v1); - if (!quot.ok()) { - return ErrorValue::From(quot.status(), context.arena()); + private: + Op op_; +}; + +class ArithmeticNegateFunction final : public Function { + public: + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.size() != 1) { + return absl::InvalidArgumentError( + "unexpected number of arguments for unary function"); + } + switch (args[0].kind()) { + case ValueKind::kInt: { + auto inv = + cel::internal::CheckedNegation(args[0].GetInt().NativeValue()); + if (!inv.ok()) { + return ErrorValue::From(inv.status(), context.arena()); + } + return IntValue(*inv); + } + case ValueKind::kDouble: + return DoubleValue(-args[0].GetDouble().NativeValue()); + default: + return absl::InvalidArgumentError("expected int or double value"); + } } - return UintValue(*quot); -} - -template <> -Value Div(double v0, double v1, const Function::InvokeContext&) { - static_assert(std::numeric_limits::is_iec559, - "Division by zero for doubles must be supported"); - - // For double, division will result in +/- inf - return DoubleValue(v0 / v1); -} - -// Modulo operation -template -Value Modulo(Type v0, Type v1, const Function::InvokeContext& context); - -// Modulo operations for integer types should check for -// division by 0 -template <> -Value Modulo(int64_t v0, int64_t v1, - const Function::InvokeContext& context) { - auto mod = cel::internal::CheckedMod(v0, v1); - if (!mod.ok()) { - return ErrorValue::From(mod.status(), context.arena()); - } - return IntValue(*mod); -} - -template <> -Value Modulo(uint64_t v0, uint64_t v1, - const Function::InvokeContext& context) { - auto mod = cel::internal::CheckedMod(v0, v1); - if (!mod.ok()) { - return ErrorValue::From(mod.status(), context.arena()); +}; + +absl::Status RegisterBinaryOverloads(FunctionRegistry& registry, + absl::string_view name, + ArithmeticBinaryFunction::Op op, + absl::Span kinds) { + for (Kind kind : kinds) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(name, /*is_receiver_style=*/false, {kind, kind}), + std::make_unique(op))); } - return UintValue(*mod); -} - -// Helper method -// Registers all arithmetic functions for template parameter type. -template -absl::Status RegisterArithmeticFunctionsForType(FunctionRegistry& registry) { - using FunctionAdapter = cel::BinaryFunctionAdapter; - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kAdd, false), - FunctionAdapter::WrapFunction(&Add))); - - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kSubtract, false), - FunctionAdapter::WrapFunction(&Sub))); - - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kMultiply, false), - FunctionAdapter::WrapFunction(&Mul))); - - return registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kDivide, false), - FunctionAdapter::WrapFunction(&Div)); + return absl::OkStatus(); } } // namespace absl::Status RegisterArithmeticFunctions(FunctionRegistry& registry, const RuntimeOptions& options) { - CEL_RETURN_IF_ERROR(RegisterArithmeticFunctionsForType(registry)); - CEL_RETURN_IF_ERROR(RegisterArithmeticFunctionsForType(registry)); - CEL_RETURN_IF_ERROR(RegisterArithmeticFunctionsForType(registry)); - - // Modulo - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter::CreateDescriptor( - cel::builtin::kModulo, false), - BinaryFunctionAdapter::WrapFunction( - &Modulo))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter::CreateDescriptor( - cel::builtin::kModulo, false), - BinaryFunctionAdapter::WrapFunction( - &Modulo))); + static constexpr Kind kNumericKinds[] = {Kind::kInt, Kind::kUint, + Kind::kDouble}; + static constexpr Kind kIntegralKinds[] = {Kind::kInt, Kind::kUint}; + + CEL_RETURN_IF_ERROR(RegisterBinaryOverloads( + registry, cel::builtin::kAdd, ArithmeticBinaryFunction::Op::kAdd, + kNumericKinds)); + CEL_RETURN_IF_ERROR(RegisterBinaryOverloads( + registry, cel::builtin::kSubtract, + ArithmeticBinaryFunction::Op::kSubtract, kNumericKinds)); + CEL_RETURN_IF_ERROR(RegisterBinaryOverloads( + registry, cel::builtin::kMultiply, + ArithmeticBinaryFunction::Op::kMultiply, kNumericKinds)); + CEL_RETURN_IF_ERROR(RegisterBinaryOverloads( + registry, cel::builtin::kDivide, ArithmeticBinaryFunction::Op::kDivide, + kNumericKinds)); + CEL_RETURN_IF_ERROR(RegisterBinaryOverloads( + registry, cel::builtin::kModulo, ArithmeticBinaryFunction::Op::kModulo, + kIntegralKinds)); // Negation group CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor(cel::builtin::kNeg, - false), - UnaryFunctionAdapter::WrapFunction( - [](int64_t value, const Function::InvokeContext& context) -> Value { - auto inv = cel::internal::CheckedNegation(value); - if (!inv.ok()) { - return ErrorValue::From(inv.status(), context.arena()); - } - return IntValue(*inv); - }))); - + FunctionDescriptor(cel::builtin::kNeg, /*is_receiver_style=*/false, + {Kind::kInt}), + std::make_unique())); return registry.Register( - UnaryFunctionAdapter::CreateDescriptor(cel::builtin::kNeg, - false), - UnaryFunctionAdapter::WrapFunction( - [](double value) -> double { return -value; })); + FunctionDescriptor(cel::builtin::kNeg, /*is_receiver_style=*/false, + {Kind::kDouble}), + std::make_unique()); } } // namespace cel diff --git a/runtime/standard/comparison_functions.cc b/runtime/standard/comparison_functions.cc index bddd1efe9..fd0c4abb6 100644 --- a/runtime/standard/comparison_functions.cc +++ b/runtime/standard/comparison_functions.cc @@ -14,15 +14,22 @@ #include "runtime/standard/comparison_functions.h" -#include +#include +#include #include "absl/status/status.h" -#include "absl/time/time.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" +#include "common/value_kind.h" #include "internal/number.h" #include "internal/status_macros.h" +#include "runtime/function.h" #include "runtime/function_registry.h" #include "runtime/runtime_options.h" @@ -32,231 +39,148 @@ namespace { using ::cel::internal::Number; -// Comparison template functions -template -bool LessThan(Type t1, Type t2) { - return (t1 < t2); -} - -template -bool LessThanOrEqual(Type t1, Type t2) { - return (t1 <= t2); -} - -template -bool GreaterThan(Type t1, Type t2) { - return LessThan(t2, t1); -} - -template -bool GreaterThanOrEqual(Type t1, Type t2) { - return LessThanOrEqual(t2, t1); -} - -// String value comparions specializations -template <> -bool LessThan(const StringValue& t1, const StringValue& t2) { - return t1.Compare(t2) < 0; -} - -template <> -bool LessThanOrEqual(const StringValue& t1, const StringValue& t2) { - return t1.Compare(t2) <= 0; -} - -template <> -bool GreaterThan(const StringValue& t1, const StringValue& t2) { - return t1.Compare(t2) > 0; -} - -template <> -bool GreaterThanOrEqual(const StringValue& t1, const StringValue& t2) { - return t1.Compare(t2) >= 0; -} - -// bytes value comparions specializations -template <> -bool LessThan(const BytesValue& t1, const BytesValue& t2) { - return t1.Compare(t2) < 0; -} - -template <> -bool LessThanOrEqual(const BytesValue& t1, const BytesValue& t2) { - return t1.Compare(t2) <= 0; -} - -template <> -bool GreaterThan(const BytesValue& t1, const BytesValue& t2) { - return t1.Compare(t2) > 0; -} - -template <> -bool GreaterThanOrEqual(const BytesValue& t1, const BytesValue& t2) { - return t1.Compare(t2) >= 0; -} - -// Duration comparison specializations -template <> -bool LessThan(absl::Duration t1, absl::Duration t2) { - return absl::operator<(t1, t2); -} - -template <> -bool LessThanOrEqual(absl::Duration t1, absl::Duration t2) { - return absl::operator<=(t1, t2); -} - -template <> -bool GreaterThan(absl::Duration t1, absl::Duration t2) { - return absl::operator>(t1, t2); -} - -template <> -bool GreaterThanOrEqual(absl::Duration t1, absl::Duration t2) { - return absl::operator>=(t1, t2); -} - -// Timestamp comparison specializations -template <> -bool LessThan(absl::Time t1, absl::Time t2) { - return absl::operator<(t1, t2); -} - -template <> -bool LessThanOrEqual(absl::Time t1, absl::Time t2) { - return absl::operator<=(t1, t2); -} - -template <> -bool GreaterThan(absl::Time t1, absl::Time t2) { - return absl::operator>(t1, t2); -} - -template <> -bool GreaterThanOrEqual(absl::Time t1, absl::Time t2) { - return absl::operator>=(t1, t2); -} - -template -bool CrossNumericLessThan(T t, U u) { - return Number(t) < Number(u); -} - -template -bool CrossNumericGreaterThan(T t, U u) { - return Number(t) > Number(u); -} - -template -bool CrossNumericLessOrEqualTo(T t, U u) { - return Number(t) <= Number(u); -} - -template -bool CrossNumericGreaterOrEqualTo(T t, U u) { - return Number(t) >= Number(u); +absl::optional NumberFromValue(const Value& value) { + switch (value.kind()) { + case ValueKind::kInt: + return Number::FromInt64(value.GetInt().NativeValue()); + case ValueKind::kUint: + return Number::FromUint64(value.GetUint().NativeValue()); + case ValueKind::kDouble: + return Number::FromDouble(value.GetDouble().NativeValue()); + default: + return absl::nullopt; + } } -template -absl::Status RegisterComparisonFunctionsForType( - cel::FunctionRegistry& registry) { - using FunctionAdapter = BinaryFunctionAdapter; - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kLess, false), - FunctionAdapter::WrapFunction(LessThan))); - - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kLessOrEqual, false), - FunctionAdapter::WrapFunction(LessThanOrEqual))); +class ComparisonFunction final : public Function { + public: + enum class Op { kLess, kLessOrEqual, kGreater, kGreaterOrEqual }; + + explicit ComparisonFunction(Op op) : op_(op) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext&) const override { + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); + } + if (args[0].kind() == args[1].kind()) { + switch (args[0].kind()) { + case ValueKind::kBool: + return BoolValue(CompareOrdered(args[0].GetBool().NativeValue(), + args[1].GetBool().NativeValue())); + case ValueKind::kInt: + return BoolValue(CompareOrdered(args[0].GetInt().NativeValue(), + args[1].GetInt().NativeValue())); + case ValueKind::kUint: + return BoolValue(CompareOrdered(args[0].GetUint().NativeValue(), + args[1].GetUint().NativeValue())); + case ValueKind::kDouble: + return BoolValue(CompareOrdered(args[0].GetDouble().NativeValue(), + args[1].GetDouble().NativeValue())); + case ValueKind::kString: { + int cmp = args[0].GetString().Compare(args[1].GetString()); + return BoolValue(CompareThreeWay(cmp)); + } + case ValueKind::kBytes: { + int cmp = args[0].GetBytes().Compare(args[1].GetBytes()); + return BoolValue(CompareThreeWay(cmp)); + } + case ValueKind::kDuration: + return BoolValue(CompareOrdered(args[0].GetDuration().ToDuration(), + args[1].GetDuration().ToDuration())); + case ValueKind::kTimestamp: + return BoolValue(CompareOrdered(args[0].GetTimestamp().ToTime(), + args[1].GetTimestamp().ToTime())); + default: + return absl::InvalidArgumentError("expected comparable value"); + } + } + + absl::optional lhs = NumberFromValue(args[0]); + absl::optional rhs = NumberFromValue(args[1]); + if (!lhs.has_value() || !rhs.has_value()) { + return absl::InvalidArgumentError("expected numeric value"); + } + return BoolValue(CompareOrdered(*lhs, *rhs)); + } - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kGreater, false), - FunctionAdapter::WrapFunction(GreaterThan))); + private: + template + bool CompareOrdered(const T& lhs, const T& rhs) const { + switch (op_) { + case Op::kLess: + return lhs < rhs; + case Op::kLessOrEqual: + return lhs <= rhs; + case Op::kGreater: + return lhs > rhs; + case Op::kGreaterOrEqual: + return lhs >= rhs; + } + } - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kGreaterOrEqual, false), - FunctionAdapter::WrapFunction(GreaterThanOrEqual))); + bool CompareThreeWay(int cmp) const { + switch (op_) { + case Op::kLess: + return cmp < 0; + case Op::kLessOrEqual: + return cmp <= 0; + case Op::kGreater: + return cmp > 0; + case Op::kGreaterOrEqual: + return cmp >= 0; + } + } + Op op_; +}; + +absl::Status RegisterComparisonOverloads(FunctionRegistry& registry, Kind lhs, + Kind rhs) { + static constexpr struct { + absl::string_view name; + ComparisonFunction::Op op; + } kOps[] = { + {cel::builtin::kLess, ComparisonFunction::Op::kLess}, + {cel::builtin::kLessOrEqual, ComparisonFunction::Op::kLessOrEqual}, + {cel::builtin::kGreater, ComparisonFunction::Op::kGreater}, + {cel::builtin::kGreaterOrEqual, ComparisonFunction::Op::kGreaterOrEqual}, + }; + for (const auto& entry : kOps) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(entry.name, /*is_receiver_style=*/false, {lhs, rhs}), + std::make_unique(entry.op))); + } return absl::OkStatus(); } absl::Status RegisterHomogenousComparisonFunctions( cel::FunctionRegistry& registry) { - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR( - RegisterComparisonFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR( - RegisterComparisonFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR( - RegisterComparisonFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - - return absl::OkStatus(); -} - -template -absl::Status RegisterCrossNumericComparisons(cel::FunctionRegistry& registry) { - using FunctionAdapter = BinaryFunctionAdapter; - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kLess, - /*receiver_style=*/false), - FunctionAdapter::WrapFunction(&CrossNumericLessThan))); - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kGreater, - /*receiver_style=*/false), - FunctionAdapter::WrapFunction(&CrossNumericGreaterThan))); - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kGreaterOrEqual, - /*receiver_style=*/false), - FunctionAdapter::WrapFunction(&CrossNumericGreaterOrEqualTo))); - CEL_RETURN_IF_ERROR(registry.Register( - FunctionAdapter::CreateDescriptor(cel::builtin::kLessOrEqual, - /*receiver_style=*/false), - FunctionAdapter::WrapFunction(&CrossNumericLessOrEqualTo))); + static constexpr Kind kOrderableKinds[] = { + Kind::kBool, Kind::kInt, Kind::kUint, Kind::kDouble, + Kind::kString, Kind::kBytes, Kind::kDuration, Kind::kTimestamp, + }; + for (Kind kind : kOrderableKinds) { + CEL_RETURN_IF_ERROR(RegisterComparisonOverloads(registry, kind, kind)); + } return absl::OkStatus(); } absl::Status RegisterHeterogeneousComparisonFunctions( cel::FunctionRegistry& registry) { - CEL_RETURN_IF_ERROR( - (RegisterCrossNumericComparisons(registry))); - CEL_RETURN_IF_ERROR( - (RegisterCrossNumericComparisons(registry))); - - CEL_RETURN_IF_ERROR( - (RegisterCrossNumericComparisons(registry))); - CEL_RETURN_IF_ERROR( - (RegisterCrossNumericComparisons(registry))); - - CEL_RETURN_IF_ERROR( - (RegisterCrossNumericComparisons(registry))); - CEL_RETURN_IF_ERROR( - (RegisterCrossNumericComparisons(registry))); - - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - CEL_RETURN_IF_ERROR( - RegisterComparisonFunctionsForType(registry)); - CEL_RETURN_IF_ERROR( - RegisterComparisonFunctionsForType(registry)); - CEL_RETURN_IF_ERROR( - RegisterComparisonFunctionsForType(registry)); - CEL_RETURN_IF_ERROR(RegisterComparisonFunctionsForType(registry)); - - return absl::OkStatus(); + static constexpr std::pair kCrossNumericKinds[] = { + {Kind::kDouble, Kind::kInt}, {Kind::kDouble, Kind::kUint}, + {Kind::kUint, Kind::kDouble}, {Kind::kUint, Kind::kInt}, + {Kind::kInt, Kind::kDouble}, {Kind::kInt, Kind::kUint}, + }; + for (const auto& pair : kCrossNumericKinds) { + CEL_RETURN_IF_ERROR( + RegisterComparisonOverloads(registry, pair.first, pair.second)); + } + return RegisterHomogenousComparisonFunctions(registry); } + } // namespace absl::Status RegisterComparisonFunctions(FunctionRegistry& registry, diff --git a/runtime/standard/container_functions.cc b/runtime/standard/container_functions.cc index c81dc7596..85a8073c2 100644 --- a/runtime/standard/container_functions.cc +++ b/runtime/standard/container_functions.cc @@ -16,16 +16,21 @@ #include #include +#include #include #include "absl/base/nullability.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" +#include "common/value_kind.h" #include "common/values/list_value_builder.h" #include "internal/status_macros.h" +#include "runtime/function.h" #include "runtime/function_registry.h" #include "runtime/runtime_options.h" #include "google/protobuf/arena.h" @@ -35,14 +40,6 @@ namespace cel { namespace { -absl::StatusOr MapSizeImpl(const MapValue& value) { - return value.Size(); -} - -absl::StatusOr ListSizeImpl(const ListValue& value) { - return value.Size(); -} - // Concatenation for CelList type. absl::StatusOr ConcatList( const ListValue& value1, const ListValue& value2, @@ -95,6 +92,64 @@ absl::StatusOr AppendList(ListValue value1, const Value& value2) { } return absl::InvalidArgumentError("Unexpected call to runtime list append."); } + +class ContainerFunction final : public Function { + public: + enum class Op { kSize, kConcatList, kAppendList }; + + explicit ContainerFunction(Op op) : op_(op) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + switch (op_) { + case Op::kSize: { + if (args.size() != 1) { + return absl::InvalidArgumentError( + "unexpected number of arguments for unary function"); + } + absl::StatusOr size; + switch (args[0].kind()) { + case ValueKind::kList: + size = args[0].GetList().Size(); + break; + case ValueKind::kMap: + size = args[0].GetMap().Size(); + break; + default: + return absl::InvalidArgumentError("expected list or map value"); + } + if (!size.ok()) { + return std::move(size).status(); + } + return IntValue(*size); + } + case Op::kConcatList: + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); + } + if (!args[0].IsList() || !args[1].IsList()) { + return absl::InvalidArgumentError("expected list value"); + } + return ConcatList(args[0].GetList(), args[1].GetList(), + context.descriptor_pool(), context.message_factory(), + context.arena()); + case Op::kAppendList: + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); + } + if (!args[0].IsList()) { + return absl::InvalidArgumentError("expected list value"); + } + return AppendList(args[0].GetList(), args[1]); + } + } + + private: + Op op_; +}; + } // namespace absl::Status RegisterContainerFunctions(FunctionRegistry& registry, @@ -102,35 +157,26 @@ absl::Status RegisterContainerFunctions(FunctionRegistry& registry, // receiver style = true/false // Support both the global and receiver style size() for lists and maps. for (bool receiver_style : {true, false}) { - CEL_RETURN_IF_ERROR(registry.Register( - cel::UnaryFunctionAdapter, const ListValue&>:: - CreateDescriptor(cel::builtin::kSize, receiver_style), - UnaryFunctionAdapter, - const ListValue&>::WrapFunction(ListSizeImpl))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter, const MapValue&>:: - CreateDescriptor(cel::builtin::kSize, receiver_style), - UnaryFunctionAdapter, - const MapValue&>::WrapFunction(MapSizeImpl))); + for (Kind kind : {Kind::kList, Kind::kMap}) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(cel::builtin::kSize, receiver_style, {kind}), + std::make_unique(ContainerFunction::Op::kSize))); + } } if (options.enable_list_concat) { CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter< - absl::StatusOr, const ListValue&, - const ListValue&>::CreateDescriptor(cel::builtin::kAdd, false), - BinaryFunctionAdapter, const ListValue&, - const ListValue&>::WrapFunction(ConcatList))); + FunctionDescriptor(cel::builtin::kAdd, /*is_receiver_style=*/false, + {Kind::kList, Kind::kList}), + std::make_unique( + ContainerFunction::Op::kConcatList))); } return registry.Register( - BinaryFunctionAdapter< - absl::StatusOr, ListValue, - const Value&>::CreateDescriptor(cel::builtin::kRuntimeListAppend, - false), - BinaryFunctionAdapter, ListValue, - const Value&>::WrapFunction(AppendList)); + FunctionDescriptor(cel::builtin::kRuntimeListAppend, + /*is_receiver_style=*/false, + {Kind::kList, Kind::kAny}), + std::make_unique(ContainerFunction::Op::kAppendList)); } } // namespace cel diff --git a/runtime/standard/container_membership_functions.cc b/runtime/standard/container_membership_functions.cc index 26c115103..eda86f154 100644 --- a/runtime/standard/container_membership_functions.cc +++ b/runtime/standard/container_membership_functions.cc @@ -16,22 +16,22 @@ #include #include +#include -#include "absl/base/nullability.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" +#include "common/value_kind.h" #include "internal/number.h" #include "internal/status_macros.h" +#include "runtime/function.h" #include "runtime/function_registry.h" -#include "runtime/register_function_helper.h" #include "runtime/runtime_options.h" -#include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" namespace cel { namespace { @@ -44,274 +44,240 @@ static constexpr std::array in_operators = { cel::builtin::kInDeprecated, // deprecated _in_ -- for backwards compat }; -template -bool ValueEquals(const Value& value, T other); - -template <> -bool ValueEquals(const Value& value, bool other) { - if (auto bool_value = value.As(); bool_value) { - return bool_value->NativeValue() == other; - } - return false; -} - -template <> -bool ValueEquals(const Value& value, int64_t other) { - if (auto int_value = value.As(); int_value) { - return int_value->NativeValue() == other; +bool HomogenousValueEquals(const Value& lhs, const Value& rhs) { + if (lhs.kind() != rhs.kind()) { + return false; } - return false; -} - -template <> -bool ValueEquals(const Value& value, uint64_t other) { - if (auto uint_value = value.As(); uint_value) { - return uint_value->NativeValue() == other; + switch (lhs.kind()) { + case ValueKind::kBool: + return lhs.GetBool().NativeValue() == rhs.GetBool().NativeValue(); + case ValueKind::kInt: + return lhs.GetInt().NativeValue() == rhs.GetInt().NativeValue(); + case ValueKind::kUint: + return lhs.GetUint().NativeValue() == rhs.GetUint().NativeValue(); + case ValueKind::kDouble: + return lhs.GetDouble().NativeValue() == rhs.GetDouble().NativeValue(); + case ValueKind::kString: + return lhs.GetString().Equals(rhs.GetString()); + case ValueKind::kBytes: + return lhs.GetBytes().Equals(rhs.GetBytes()); + default: + return false; } - return false; } -template <> -bool ValueEquals(const Value& value, double other) { - if (auto double_value = value.As(); double_value) { - return double_value->NativeValue() == other; +absl::StatusOr EvaluateHomogenousListIn( + const Value& key, const ListValue& list, + const Function::InvokeContext& context) { + switch (key.kind()) { + case ValueKind::kBool: + case ValueKind::kInt: + case ValueKind::kUint: + case ValueKind::kDouble: + case ValueKind::kString: + case ValueKind::kBytes: + break; + default: + return absl::InvalidArgumentError("unsupported key kind for list in"); } - return false; -} - -template <> -bool ValueEquals(const Value& value, const StringValue& other) { - if (auto string_value = value.As(); string_value) { - return string_value->Equals(other); - } - return false; -} - -template <> -bool ValueEquals(const Value& value, const BytesValue& other) { - if (auto bytes_value = value.As(); bytes_value) { - return bytes_value->Equals(other); - } - return false; -} - -// Template function implementing CEL in() function -template -absl::StatusOr In( - T value, const ListValue& list, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { CEL_ASSIGN_OR_RETURN(auto size, list.Size()); Value element; for (int i = 0; i < size; i++) { - CEL_RETURN_IF_ERROR( - list.Get(i, descriptor_pool, message_factory, arena, &element)); - if (ValueEquals(element, value)) { - return true; + CEL_RETURN_IF_ERROR(list.Get(i, context.descriptor_pool(), + context.message_factory(), context.arena(), + &element)); + if (HomogenousValueEquals(element, key)) { + return BoolValue(true); } } - - return false; + return BoolValue(false); } -// Implementation for @in operator using heterogeneous equality. -absl::StatusOr HeterogeneousEqualityIn( - const Value& value, const ListValue& list, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - return list.Contains(value, descriptor_pool, message_factory, arena); -} - -absl::Status RegisterListMembershipFunctions(FunctionRegistry& registry, - const RuntimeOptions& options) { - for (absl::string_view op : in_operators) { - if (options.enable_heterogeneous_equality) { - CEL_RETURN_IF_ERROR( - (RegisterHelper, const Value&, const ListValue&>>:: - RegisterGlobalOverload(op, &HeterogeneousEqualityIn, registry))); - } else { - CEL_RETURN_IF_ERROR( - (RegisterHelper, bool, - const ListValue&>>:: - RegisterGlobalOverload(op, In, registry))); - CEL_RETURN_IF_ERROR( - (RegisterHelper, int64_t, - const ListValue&>>:: - RegisterGlobalOverload(op, In, registry))); - CEL_RETURN_IF_ERROR( - (RegisterHelper, uint64_t, - const ListValue&>>:: - RegisterGlobalOverload(op, In, registry))); - CEL_RETURN_IF_ERROR( - (RegisterHelper, double, - const ListValue&>>:: - RegisterGlobalOverload(op, In, registry))); - CEL_RETURN_IF_ERROR( - (RegisterHelper, const StringValue&, const ListValue&>>:: - RegisterGlobalOverload(op, In, registry))); - CEL_RETURN_IF_ERROR( - (RegisterHelper, const BytesValue&, const ListValue&>>:: - RegisterGlobalOverload(op, In, registry))); +absl::StatusOr EvaluateMapIn(const Value& key, const MapValue& map_value, + bool enable_heterogeneous_equality, + const Function::InvokeContext& context) { + switch (key.kind()) { + case ValueKind::kString: { + Value result; + CEL_RETURN_IF_ERROR(map_value.Has(key, context.descriptor_pool(), + context.message_factory(), + context.arena(), &result)); + if (result.IsBool()) { + return result; + } + if (enable_heterogeneous_equality) { + return BoolValue(false); + } + return result; } - } - return absl::OkStatus(); -} - -absl::Status RegisterMapMembershipFunctions(FunctionRegistry& registry, - const RuntimeOptions& options) { - const bool enable_heterogeneous_equality = - options.enable_heterogeneous_equality; - - auto boolKeyInSet = - [enable_heterogeneous_equality]( - bool key, const MapValue& map_value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr { - Value has; - CEL_RETURN_IF_ERROR(map_value.Has(BoolValue(key), descriptor_pool, - message_factory, arena, &has)); - if (has.IsTrue()) { + case ValueKind::kBool: { + Value has; + CEL_RETURN_IF_ERROR(map_value.Has(key, context.descriptor_pool(), + context.message_factory(), + context.arena(), &has)); + if (has.IsTrue()) { + return has; + } + if (enable_heterogeneous_equality) { + return BoolValue(false); + } return has; } - if (enable_heterogeneous_equality) { - return BoolValue(false); - } - return has; - }; - - auto intKeyInSet = - [enable_heterogeneous_equality]( - int64_t key, const MapValue& map_value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr { - Value result; - CEL_RETURN_IF_ERROR(map_value.Has(IntValue(key), descriptor_pool, - message_factory, arena, &result)); - if (enable_heterogeneous_equality) { - if (result.IsTrue()) { - return result; - } - Number number = Number::FromInt64(key); - if (number.LosslessConvertibleToUint()) { - Value result_alt; - CEL_RETURN_IF_ERROR(map_value.Has(UintValue(number.AsUint()), - descriptor_pool, message_factory, - arena, &result_alt)); - if (result_alt.IsTrue()) { - return result_alt; + case ValueKind::kInt: { + Value result; + CEL_RETURN_IF_ERROR(map_value.Has(key, context.descriptor_pool(), + context.message_factory(), + context.arena(), &result)); + if (enable_heterogeneous_equality) { + if (result.IsTrue()) { + return result; } + Number number = Number::FromInt64(key.GetInt().NativeValue()); + if (number.LosslessConvertibleToUint()) { + Value result_alt; + CEL_RETURN_IF_ERROR(map_value.Has( + UintValue(number.AsUint()), context.descriptor_pool(), + context.message_factory(), context.arena(), &result_alt)); + if (result_alt.IsTrue()) { + return result_alt; + } + } + return BoolValue(false); } - return BoolValue(false); - } - return result; - }; - - auto stringKeyInSet = - [enable_heterogeneous_equality]( - const StringValue& key, const MapValue& map_value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr { - Value result; - CEL_RETURN_IF_ERROR( - map_value.Has(key, descriptor_pool, message_factory, arena, &result)); - if (result.IsBool()) { return result; } - if (enable_heterogeneous_equality) { - return BoolValue(false); + case ValueKind::kUint: { + Value has; + CEL_RETURN_IF_ERROR(map_value.Has(key, context.descriptor_pool(), + context.message_factory(), + context.arena(), &has)); + if (enable_heterogeneous_equality) { + if (has.IsTrue()) { + return has; + } + Number number = Number::FromUint64(key.GetUint().NativeValue()); + if (number.LosslessConvertibleToInt()) { + Value has_alt; + CEL_RETURN_IF_ERROR(map_value.Has( + IntValue(number.AsInt()), context.descriptor_pool(), + context.message_factory(), context.arena(), &has_alt)); + if (has_alt.IsTrue()) { + return has_alt; + } + } + return BoolValue(false); + } + return has; } - return result; - }; - - auto uintKeyInSet = - [enable_heterogeneous_equality]( - uint64_t key, const MapValue& map_value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr { - Value has; - CEL_RETURN_IF_ERROR(map_value.Has(UintValue(key), descriptor_pool, - message_factory, arena, &has)); - if (enable_heterogeneous_equality) { - if (has.IsTrue()) { - return has; + case ValueKind::kDouble: { + if (!enable_heterogeneous_equality) { + return absl::InvalidArgumentError( + "double key not supported for homogenous map in"); } - Value has_alt; - Number number = Number::FromUint64(key); + Number number = Number::FromDouble(key.GetDouble().NativeValue()); if (number.LosslessConvertibleToInt()) { - CEL_RETURN_IF_ERROR(map_value.Has(IntValue(number.AsInt()), - descriptor_pool, message_factory, - arena, &has_alt)); + Value has; + CEL_RETURN_IF_ERROR( + map_value.Has(IntValue(number.AsInt()), context.descriptor_pool(), + context.message_factory(), context.arena(), &has)); + if (has.IsTrue()) { + return has; + } + } + if (number.LosslessConvertibleToUint()) { + Value has; + CEL_RETURN_IF_ERROR( + map_value.Has(UintValue(number.AsUint()), context.descriptor_pool(), + context.message_factory(), context.arena(), &has)); if (has.IsTrue()) { return has; } } return BoolValue(false); } - return has; - }; + default: + return absl::InvalidArgumentError("unsupported key kind for map in"); + } +} - auto doubleKeyInSet = - [](double key, const MapValue& map_value, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr { - Number number = Number::FromDouble(key); - if (number.LosslessConvertibleToInt()) { - Value has; - CEL_RETURN_IF_ERROR(map_value.Has(IntValue(number.AsInt()), - descriptor_pool, message_factory, arena, - &has)); - if (has.IsTrue()) { - return has; - } +class ContainerMembershipFunction final : public Function { + public: + explicit ContainerMembershipFunction(bool enable_heterogeneous_equality) + : enable_heterogeneous_equality_(enable_heterogeneous_equality) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); } - if (number.LosslessConvertibleToUint()) { - Value has; - CEL_RETURN_IF_ERROR(map_value.Has(UintValue(number.AsUint()), - descriptor_pool, message_factory, arena, - &has)); - if (has.IsTrue()) { - return has; - } + switch (args[1].kind()) { + case ValueKind::kList: + if (enable_heterogeneous_equality_) { + return args[1].GetList().Contains(args[0], context.descriptor_pool(), + context.message_factory(), + context.arena()); + } + return EvaluateHomogenousListIn(args[0], args[1].GetList(), context); + case ValueKind::kMap: + return EvaluateMapIn(args[0], args[1].GetMap(), + enable_heterogeneous_equality_, context); + default: + return absl::InvalidArgumentError("expected list or map value"); } - return BoolValue(false); - }; - - for (auto op : in_operators) { - auto status = RegisterHelper, const StringValue&, - const MapValue&>>::RegisterGlobalOverload(op, stringKeyInSet, registry); - if (!status.ok()) return status; - - status = RegisterHelper< - BinaryFunctionAdapter, bool, const MapValue&>>:: - RegisterGlobalOverload(op, boolKeyInSet, registry); - if (!status.ok()) return status; + } - status = RegisterHelper, - int64_t, const MapValue&>>:: - RegisterGlobalOverload(op, intKeyInSet, registry); - if (!status.ok()) return status; + private: + bool enable_heterogeneous_equality_; +}; - status = RegisterHelper, - uint64_t, const MapValue&>>:: - RegisterGlobalOverload(op, uintKeyInSet, registry); - if (!status.ok()) return status; +absl::Status RegisterListMembershipFunctions(FunctionRegistry& registry, + const RuntimeOptions& options) { + static constexpr Kind kHomogenousListKeyKinds[] = { + Kind::kBool, Kind::kInt, Kind::kUint, + Kind::kDouble, Kind::kString, Kind::kBytes, + }; + for (absl::string_view op : in_operators) { + if (options.enable_heterogeneous_equality) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(op, /*is_receiver_style=*/false, + {Kind::kAny, Kind::kList}), + std::make_unique(true))); + } else { + for (Kind kind : kHomogenousListKeyKinds) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(op, /*is_receiver_style=*/false, + {kind, Kind::kList}), + std::make_unique(false))); + } + } + } + return absl::OkStatus(); +} +absl::Status RegisterMapMembershipFunctions(FunctionRegistry& registry, + const RuntimeOptions& options) { + const bool enable_heterogeneous_equality = + options.enable_heterogeneous_equality; + static constexpr Kind kMapKeyKinds[] = { + Kind::kString, + Kind::kBool, + Kind::kInt, + Kind::kUint, + }; + for (absl::string_view op : in_operators) { + for (Kind kind : kMapKeyKinds) { + CEL_RETURN_IF_ERROR( + registry.Register(FunctionDescriptor(op, /*is_receiver_style=*/false, + {kind, Kind::kMap}), + std::make_unique( + enable_heterogeneous_equality))); + } if (enable_heterogeneous_equality) { - status = RegisterHelper, - double, const MapValue&>>:: - RegisterGlobalOverload(op, doubleKeyInSet, registry); - if (!status.ok()) return status; + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(op, /*is_receiver_style=*/false, + {Kind::kDouble, Kind::kMap}), + std::make_unique(true))); } } return absl::OkStatus(); diff --git a/runtime/standard/equality_functions.cc b/runtime/standard/equality_functions.cc index 03e29223f..0a9dc72b9 100644 --- a/runtime/standard/equality_functions.cc +++ b/runtime/standard/equality_functions.cc @@ -15,27 +15,26 @@ #include "runtime/standard/equality_functions.h" #include -#include -#include -#include +#include #include #include "absl/base/nullability.h" -#include "absl/functional/function_ref.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "absl/types/optional.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" #include "common/value_kind.h" #include "internal/number.h" #include "internal/status_macros.h" +#include "runtime/function.h" #include "runtime/function_registry.h" #include "runtime/internal/errors.h" -#include "runtime/register_function_helper.h" #include "runtime/runtime_options.h" #include "google/protobuf/arena.h" #include "google/protobuf/descriptor.h" @@ -72,57 +71,6 @@ struct HeterogeneousEqualProvider { google::protobuf::Arena* absl_nonnull arena) const; }; -// Comparison template functions -template -absl::optional Inequal(Type lhs, Type rhs) { - return lhs != rhs; -} - -template <> -absl::optional Inequal(const StringValue& lhs, const StringValue& rhs) { - return !lhs.Equals(rhs); -} - -template <> -absl::optional Inequal(const BytesValue& lhs, const BytesValue& rhs) { - return !lhs.Equals(rhs); -} - -template <> -absl::optional Inequal(const NullValue&, const NullValue&) { - return false; -} - -template <> -absl::optional Inequal(const TypeValue& lhs, const TypeValue& rhs) { - return lhs.name() != rhs.name(); -} - -template -absl::optional Equal(Type lhs, Type rhs) { - return lhs == rhs; -} - -template <> -absl::optional Equal(const StringValue& lhs, const StringValue& rhs) { - return lhs.Equals(rhs); -} - -template <> -absl::optional Equal(const BytesValue& lhs, const BytesValue& rhs) { - return lhs.Equals(rhs); -} - -template <> -absl::optional Equal(const NullValue&, const NullValue&) { - return true; -} - -template <> -absl::optional Equal(const TypeValue& lhs, const TypeValue& rhs) { - return lhs.name() == rhs.name(); -} - // Equality for lists. Template parameter provides either heterogeneous or // homogenous equality for comparing members. template @@ -181,7 +129,7 @@ absl::optional NumberFromValue(const Value& value) { return Number::FromDouble(value.GetDouble().NativeValue()); } - return std::nullopt; + return absl::nullopt; } absl::StatusOr> CheckAlternativeNumericType( @@ -192,7 +140,7 @@ absl::StatusOr> CheckAlternativeNumericType( absl::optional number = NumberFromValue(key); if (!number.has_value()) { - return std::nullopt; + return absl::nullopt; } if (!key.IsInt() && number->LosslessConvertibleToInt()) { @@ -215,7 +163,7 @@ absl::StatusOr> CheckAlternativeNumericType( } } - return std::nullopt; + return absl::nullopt; } // Equality for maps. Template parameter provides either heterogeneous or @@ -266,174 +214,6 @@ absl::StatusOr> MapEqual( return true; } -// Helper for wrapping ==/!= implementations. -// Name should point to a static constexpr string so the lambda capture is safe. -template -std::function -WrapComparison(Op op, absl::string_view name) { - return [op = std::move(op), name]( - Type lhs, Type rhs, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) -> Value { - absl::optional result = op(lhs, rhs); - - if (result.has_value()) { - return BoolValue(*result); - } - - return ErrorValue::From( - cel::runtime_internal::CreateNoMatchingOverloadError(name), arena); - }; -} - -// Helper method -// -// Registers all equality functions for template parameters type. -template -absl::Status RegisterEqualityFunctionsForType(cel::FunctionRegistry& registry) { - using FunctionAdapter = - cel::RegisterHelper>; - // Inequality - CEL_RETURN_IF_ERROR(FunctionAdapter::RegisterGlobalOverload( - kInequal, WrapComparison(&Inequal, kInequal), registry)); - - // Equality - CEL_RETURN_IF_ERROR(FunctionAdapter::RegisterGlobalOverload( - kEqual, WrapComparison(&Equal, kEqual), registry)); - - return absl::OkStatus(); -} - -template -auto ComplexEquality(Op&& op) { - return [op = std::forward(op)]( - const Type& t1, const Type& t2, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr { - CEL_ASSIGN_OR_RETURN(absl::optional result, - op(t1, t2, descriptor_pool, message_factory, arena)); - if (!result.has_value()) { - return ErrorValue::From( - cel::runtime_internal::CreateNoMatchingOverloadError(kEqual), arena); - } - return BoolValue(*result); - }; -} - -template -auto ComplexInequality(Op&& op) { - return [op = std::forward(op)]( - Type t1, Type t2, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) -> absl::StatusOr { - CEL_ASSIGN_OR_RETURN(absl::optional result, - op(t1, t2, descriptor_pool, message_factory, arena)); - if (!result.has_value()) { - return ErrorValue::From( - cel::runtime_internal::CreateNoMatchingOverloadError(kInequal), - arena); - } - return BoolValue(!*result); - }; -} - -template -absl::Status RegisterComplexEqualityFunctionsForType( - absl::FunctionRef>( - Type, Type, const google::protobuf::DescriptorPool* absl_nonnull, - google::protobuf::MessageFactory* absl_nonnull, google::protobuf::Arena* absl_nonnull)> - op, - cel::FunctionRegistry& registry) { - using FunctionAdapter = cel::RegisterHelper< - BinaryFunctionAdapter, Type, Type>>; - // Inequality - CEL_RETURN_IF_ERROR(FunctionAdapter::RegisterGlobalOverload( - kInequal, ComplexInequality(op), registry)); - - // Equality - CEL_RETURN_IF_ERROR(FunctionAdapter::RegisterGlobalOverload( - kEqual, ComplexEquality(op), registry)); - - return absl::OkStatus(); -} - -absl::Status RegisterHomogenousEqualityFunctions( - cel::FunctionRegistry& registry) { - CEL_RETURN_IF_ERROR(RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR(RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR(RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR(RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR( - RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR( - RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR( - RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR(RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR( - RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR( - RegisterEqualityFunctionsForType(registry)); - - CEL_RETURN_IF_ERROR( - RegisterComplexEqualityFunctionsForType( - &ListEqual, registry)); - - CEL_RETURN_IF_ERROR( - RegisterComplexEqualityFunctionsForType( - &MapEqual, registry)); - - return absl::OkStatus(); -} - -absl::Status RegisterNullMessageEqualityFunctions(FunctionRegistry& registry) { - // equals - CEL_RETURN_IF_ERROR( - (cel::RegisterHelper< - BinaryFunctionAdapter>:: - RegisterGlobalOverload( - kEqual, - [](const StructValue&, const NullValue&) { return false; }, - registry))); - - CEL_RETURN_IF_ERROR( - (cel::RegisterHelper< - BinaryFunctionAdapter>:: - RegisterGlobalOverload( - kEqual, - [](const NullValue&, const StructValue&) { return false; }, - registry))); - - // inequals - CEL_RETURN_IF_ERROR( - (cel::RegisterHelper< - BinaryFunctionAdapter>:: - RegisterGlobalOverload( - kInequal, - [](const StructValue&, const NullValue&) { return true; }, - registry))); - - return cel::RegisterHelper< - BinaryFunctionAdapter>:: - RegisterGlobalOverload( - kInequal, [](const NullValue&, const StructValue&) { return true; }, - registry); -} - template absl::StatusOr> HomogenousValueEqual( const Value& v1, const Value& v2, @@ -441,39 +221,33 @@ absl::StatusOr> HomogenousValueEqual( google::protobuf::MessageFactory* absl_nonnull message_factory, google::protobuf::Arena* absl_nonnull arena) { if (v1.kind() != v2.kind()) { - return std::nullopt; + return absl::nullopt; } static_assert(std::is_lvalue_reference_v, "unexpected value copy"); - switch (v1->kind()) { + switch (v1.kind()) { case ValueKind::kBool: - return Equal(v1.GetBool().NativeValue(), - v2.GetBool().NativeValue()); + return v1.GetBool().NativeValue() == v2.GetBool().NativeValue(); case ValueKind::kNull: - return Equal(v1.GetNull(), v2.GetNull()); + return true; case ValueKind::kInt: - return Equal(v1.GetInt().NativeValue(), - v2.GetInt().NativeValue()); + return v1.GetInt().NativeValue() == v2.GetInt().NativeValue(); case ValueKind::kUint: - return Equal(v1.GetUint().NativeValue(), - v2.GetUint().NativeValue()); + return v1.GetUint().NativeValue() == v2.GetUint().NativeValue(); case ValueKind::kDouble: - return Equal(v1.GetDouble().NativeValue(), - v2.GetDouble().NativeValue()); + return v1.GetDouble().NativeValue() == v2.GetDouble().NativeValue(); case ValueKind::kDuration: - return Equal(v1.GetDuration().NativeValue(), - v2.GetDuration().NativeValue()); + return v1.GetDuration().ToDuration() == v2.GetDuration().ToDuration(); case ValueKind::kTimestamp: - return Equal(v1.GetTimestamp().NativeValue(), - v2.GetTimestamp().NativeValue()); + return v1.GetTimestamp().ToTime() == v2.GetTimestamp().ToTime(); case ValueKind::kCelType: - return Equal(v1.GetType(), v2.GetType()); + return v1.GetType().name() == v2.GetType().name(); case ValueKind::kString: - return Equal(v1.GetString(), v2.GetString()); + return v1.GetString().Equals(v2.GetString()); case ValueKind::kBytes: - return Equal(v1.GetBytes(), v2.GetBytes()); + return v1.GetBytes().Equals(v2.GetBytes()); case ValueKind::kList: return ListEqual(v1.GetList(), v2.GetList(), descriptor_pool, message_factory, arena); @@ -484,51 +258,108 @@ absl::StatusOr> HomogenousValueEqual( return OpaqueEqual(v1.GetOpaque(), v2.GetOpaque(), descriptor_pool, message_factory, arena); default: - return std::nullopt; + return absl::nullopt; } } -absl::StatusOr EqualOverloadImpl( - const Value& lhs, const Value& rhs, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - CEL_ASSIGN_OR_RETURN(absl::optional result, - runtime_internal::ValueEqualImpl( - lhs, rhs, descriptor_pool, message_factory, arena)); - if (result.has_value()) { - return BoolValue(*result); +class HomogenousEqualityFunction final : public Function { + public: + explicit HomogenousEqualityFunction(bool negation) : negation_(negation) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); + } + if ((args[0].IsStruct() && args[1].IsNull()) || + (args[0].IsNull() && args[1].IsStruct())) { + return BoolValue(negation_); + } + CEL_ASSIGN_OR_RETURN(absl::optional result, + HomogenousValueEqual( + args[0], args[1], context.descriptor_pool(), + context.message_factory(), context.arena())); + if (!result.has_value()) { + return ErrorValue::From( + cel::runtime_internal::CreateNoMatchingOverloadError( + negation_ ? kInequal : kEqual), + context.arena()); + } + return BoolValue(negation_ ? !*result : *result); } - return ErrorValue::From( - cel::runtime_internal::CreateNoMatchingOverloadError(kEqual), arena); -} -absl::StatusOr InequalOverloadImpl( - const Value& lhs, const Value& rhs, - const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, - google::protobuf::MessageFactory* absl_nonnull message_factory, - google::protobuf::Arena* absl_nonnull arena) { - CEL_ASSIGN_OR_RETURN(absl::optional result, - runtime_internal::ValueEqualImpl( - lhs, rhs, descriptor_pool, message_factory, arena)); - if (result.has_value()) { - return BoolValue(!*result); + private: + bool negation_; +}; + +class HeterogeneousEqualityFunction final : public Function { + public: + explicit HeterogeneousEqualityFunction(bool negation) : negation_(negation) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); + } + CEL_ASSIGN_OR_RETURN(absl::optional result, + runtime_internal::ValueEqualImpl( + args[0], args[1], context.descriptor_pool(), + context.message_factory(), context.arena())); + if (!result.has_value()) { + return ErrorValue::From( + cel::runtime_internal::CreateNoMatchingOverloadError( + negation_ ? kInequal : kEqual), + context.arena()); + } + return BoolValue(negation_ ? !*result : *result); } - return ErrorValue::From( - cel::runtime_internal::CreateNoMatchingOverloadError(kInequal), arena); + + private: + bool negation_; +}; + +absl::Status RegisterHomogenousPair(FunctionRegistry& registry, Kind lhs, + Kind rhs) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(kInequal, /*is_receiver_style=*/false, {lhs, rhs}), + std::make_unique(/*negation=*/true))); + return registry.Register( + FunctionDescriptor(kEqual, /*is_receiver_style=*/false, {lhs, rhs}), + std::make_unique(/*negation=*/false)); } -absl::Status RegisterHeterogeneousEqualityFunctions( +absl::Status RegisterHomogenousEqualityFunctions( cel::FunctionRegistry& registry) { - using Adapter = cel::RegisterHelper< - BinaryFunctionAdapter, const Value&, const Value&>>; - CEL_RETURN_IF_ERROR( - Adapter::RegisterGlobalOverload(kEqual, &EqualOverloadImpl, registry)); + static constexpr Kind kKinds[] = { + Kind::kBool, Kind::kInt, Kind::kUint, Kind::kDouble, + Kind::kString, Kind::kBytes, Kind::kDuration, Kind::kTimestamp, + Kind::kNull, Kind::kType, Kind::kList, Kind::kMap, + }; + for (Kind kind : kKinds) { + CEL_RETURN_IF_ERROR(RegisterHomogenousPair(registry, kind, kind)); + } + return absl::OkStatus(); +} - CEL_RETURN_IF_ERROR(Adapter::RegisterGlobalOverload( - kInequal, &InequalOverloadImpl, registry)); +absl::Status RegisterNullMessageEqualityFunctions(FunctionRegistry& registry) { + CEL_RETURN_IF_ERROR( + RegisterHomogenousPair(registry, Kind::kStruct, Kind::kNull)); + return RegisterHomogenousPair(registry, Kind::kNull, Kind::kStruct); +} - return absl::OkStatus(); +absl::Status RegisterHeterogeneousEqualityFunctions( + cel::FunctionRegistry& registry) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(kEqual, /*is_receiver_style=*/false, + {Kind::kAny, Kind::kAny}), + std::make_unique(/*negation=*/false))); + + return registry.Register( + FunctionDescriptor(kInequal, /*is_receiver_style=*/false, + {Kind::kAny, Kind::kAny}), + std::make_unique(/*negation=*/true)); } absl::StatusOr> HomogenousEqualProvider::operator()( @@ -583,7 +414,7 @@ absl::StatusOr> ValueEqualImpl( // map containing an Error. Return no matching overload to propagate an error // instead of a false result. if (v1.IsError() || v1.IsUnknown() || v2.IsError() || v2.IsUnknown()) { - return std::nullopt; + return absl::nullopt; } return false; diff --git a/runtime/standard/logical_functions.cc b/runtime/standard/logical_functions.cc index 6eed8b695..ff2e09f91 100644 --- a/runtime/standard/logical_functions.cc +++ b/runtime/standard/logical_functions.cc @@ -14,16 +14,19 @@ #include "runtime/standard/logical_functions.h" +#include + #include "absl/status/status.h" -#include "absl/strings/string_view.h" +#include "absl/status/statusor.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" #include "internal/status_macros.h" #include "runtime/function.h" #include "runtime/function_registry.h" #include "runtime/internal/errors.h" -#include "runtime/register_function_helper.h" #include "runtime/runtime_options.h" namespace cel { @@ -31,40 +34,67 @@ namespace { using ::cel::runtime_internal::CreateNoMatchingOverloadError; -Value NotStrictlyFalseImpl(const Value& value, - const Function::InvokeContext& context) { - if (value.IsBool()) { - return value; +class LogicalNotFunction final : public Function { + public: + absl::StatusOr Invoke(absl::Span args, + const InvokeContext&) const override { + if (args.size() != 1) { + return absl::InvalidArgumentError( + "unexpected number of arguments for unary function"); + } + if (!args[0].IsBool()) { + return absl::InvalidArgumentError("expected bool value"); + } + return BoolValue(!args[0].GetBool().NativeValue()); } +}; - if (value.IsError() || value.IsUnknown()) { - return TrueValue(); - } +class NotStrictlyFalseFunction final : public Function { + public: + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.size() != 1) { + return absl::InvalidArgumentError( + "unexpected number of arguments for unary function"); + } + const Value& value = args[0]; + if (value.IsBool()) { + return value; + } - // Should only accept bool unknown or error. - return ErrorValue::From( - CreateNoMatchingOverloadError(builtin::kNotStrictlyFalse), - context.arena()); -} + if (value.IsError() || value.IsUnknown()) { + return TrueValue(); + } + + // Should only accept bool unknown or error. + return ErrorValue::From( + CreateNoMatchingOverloadError(builtin::kNotStrictlyFalse), + context.arena()); + } +}; } // namespace absl::Status RegisterLogicalFunctions(FunctionRegistry& registry, const RuntimeOptions& options) { // logical NOT - CEL_RETURN_IF_ERROR( - (RegisterHelper>::RegisterGlobalOverload( - builtin::kNot, [](bool value) -> bool { return !value; }, registry))); + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(builtin::kNot, /*is_receiver_style=*/false, + {Kind::kBool}), + std::make_unique())); // Strictness - using StrictnessHelper = RegisterHelper>; - CEL_RETURN_IF_ERROR(StrictnessHelper::RegisterNonStrictOverload( - builtin::kNotStrictlyFalse, &NotStrictlyFalseImpl, registry)); - - CEL_RETURN_IF_ERROR(StrictnessHelper::RegisterNonStrictOverload( - builtin::kNotStrictlyFalseDeprecated, &NotStrictlyFalseImpl, registry)); + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(builtin::kNotStrictlyFalse, + /*is_receiver_style=*/false, {Kind::kAny}, + /*is_strict=*/false), + std::make_unique())); - return absl::OkStatus(); + return registry.Register( + FunctionDescriptor(builtin::kNotStrictlyFalseDeprecated, + /*is_receiver_style=*/false, {Kind::kAny}, + /*is_strict=*/false), + std::make_unique()); } } // namespace cel diff --git a/runtime/standard/regex_functions.cc b/runtime/standard/regex_functions.cc index e51ef821f..4c4075b7b 100644 --- a/runtime/standard/regex_functions.cc +++ b/runtime/standard/regex_functions.cc @@ -13,10 +13,14 @@ // limitations under the License. #include "runtime/standard/regex_functions.h" +#include + #include "absl/status/status.h" -#include "absl/strings/string_view.h" +#include "absl/status/statusor.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" #include "internal/re2_options.h" #include "internal/status_macros.h" @@ -26,29 +30,43 @@ #include "re2/re2.h" namespace cel { -namespace {} // namespace +namespace { + +class RegexMatchFunction final : public Function { + public: + explicit RegexMatchFunction(int max_size) : max_size_(max_size) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); + } + if (!args[0].IsString() || !args[1].IsString()) { + return absl::InvalidArgumentError("expected string value"); + } + RE2 re2(args[1].GetString().ToString(), cel::internal::MakeRE2Options()); + CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, max_size_)) + .With(ErrorValueReturn(context.arena())); + return BoolValue(RE2::PartialMatch(args[0].GetString().ToString(), re2)); + } + + private: + int max_size_; +}; + +} // namespace absl::Status RegisterRegexFunctions(FunctionRegistry& registry, const RuntimeOptions& options) { if (options.enable_regex) { - auto regex_matches = [max_size = options.regex_max_program_size]( - const StringValue& target, - const StringValue& regex, - const Function::InvokeContext& context) -> Value { - RE2 re2(regex.ToString(), cel::internal::MakeRE2Options()); - CEL_RETURN_IF_ERROR(cel::internal::CheckRE2(re2, max_size)) - .With(ErrorValueReturn(context.arena())); - return BoolValue(RE2::PartialMatch(target.ToString(), re2)); - }; - // bind str.matches(re) and matches(str, re) for (bool receiver_style : {true, false}) { - using MatchFnAdapter = - BinaryFunctionAdapter; - CEL_RETURN_IF_ERROR( - registry.Register(MatchFnAdapter::CreateDescriptor( - cel::builtin::kRegexMatch, receiver_style), - MatchFnAdapter::WrapFunction(regex_matches))); + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(cel::builtin::kRegexMatch, receiver_style, + {Kind::kString, Kind::kString}), + std::make_unique( + options.regex_max_program_size))); } } // if options.enable_regex diff --git a/runtime/standard/string_functions.cc b/runtime/standard/string_functions.cc index 2bcfe185c..f383ee75b 100644 --- a/runtime/standard/string_functions.cc +++ b/runtime/standard/string_functions.cc @@ -14,94 +14,109 @@ #include "runtime/standard/string_functions.h" -#include +#include -#include "absl/base/nullability.h" #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" +#include "common/value_kind.h" #include "internal/status_macros.h" +#include "runtime/function.h" #include "runtime/function_registry.h" #include "runtime/runtime_options.h" -#include "google/protobuf/arena.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/message.h" namespace cel { namespace { -// Concatenation for string type. -absl::StatusOr ConcatString( - const StringValue& value1, const StringValue& value2, - const google::protobuf::DescriptorPool* absl_nonnull, - google::protobuf::MessageFactory* absl_nonnull, google::protobuf::Arena* absl_nonnull arena) { - return StringValue::Concat(value1, value2, arena); -} - -// Concatenation for bytes type. -absl::StatusOr ConcatBytes( - const BytesValue& value1, const BytesValue& value2, - const google::protobuf::DescriptorPool* absl_nonnull, - google::protobuf::MessageFactory* absl_nonnull, google::protobuf::Arena* absl_nonnull arena) { - return BytesValue::Concat(value1, value2, arena); -} - -bool StringContains(const StringValue& value, const StringValue& substr) { - return value.Contains(substr); -} +class StringFunction final : public Function { + public: + enum class Op { kContains, kEndsWith, kStartsWith, kConcat, kSize }; + + explicit StringFunction(Op op) : op_(op) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (op_ == Op::kSize) { + if (args.size() != 1) { + return absl::InvalidArgumentError( + "unexpected number of arguments for unary function"); + } + switch (args[0].kind()) { + case ValueKind::kString: + return IntValue(args[0].GetString().Size()); + case ValueKind::kBytes: + return IntValue(args[0].GetBytes().Size()); + default: + return absl::InvalidArgumentError("expected string or bytes value"); + } + } + + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); + } + if (op_ == Op::kConcat) { + if (args[0].kind() != args[1].kind()) { + return absl::InvalidArgumentError("expected matching types for concat"); + } + switch (args[0].kind()) { + case ValueKind::kString: + return StringValue::Concat(args[0].GetString(), args[1].GetString(), + context.arena()); + case ValueKind::kBytes: + return BytesValue::Concat(args[0].GetBytes(), args[1].GetBytes(), + context.arena()); + default: + return absl::InvalidArgumentError("expected string or bytes value"); + } + } + + if (!args[0].IsString() || !args[1].IsString()) { + return absl::InvalidArgumentError("expected string value"); + } + const StringValue& value = args[0].GetString(); + const StringValue& substr = args[1].GetString(); + switch (op_) { + case Op::kContains: + return BoolValue(value.Contains(substr)); + case Op::kEndsWith: + return BoolValue(value.EndsWith(substr)); + case Op::kStartsWith: + return BoolValue(value.StartsWith(substr)); + case Op::kConcat: + case Op::kSize: + return absl::InternalError("unreachable"); + } + } -bool StringEndsWith(const StringValue& value, const StringValue& suffix) { - return value.EndsWith(suffix); -} - -bool StringStartsWith(const StringValue& value, const StringValue& prefix) { - return value.StartsWith(prefix); -} + private: + Op op_; +}; absl::Status RegisterSizeFunctions(FunctionRegistry& registry) { - // String size - auto size_func = [](const StringValue& value) -> int64_t { - return value.Size(); - }; - - // Support global and receiver style size() operations on strings. - using StrSizeFnAdapter = UnaryFunctionAdapter; - CEL_RETURN_IF_ERROR(StrSizeFnAdapter::RegisterGlobalOverload( - cel::builtin::kSize, size_func, registry)); - - CEL_RETURN_IF_ERROR(StrSizeFnAdapter::RegisterMemberOverload( - cel::builtin::kSize, size_func, registry)); - - // Bytes size - auto bytes_size_func = [](const BytesValue& value) -> int64_t { - return value.Size(); - }; - - // Support global and receiver style size() operations on bytes. - using BytesSizeFnAdapter = UnaryFunctionAdapter; - CEL_RETURN_IF_ERROR(BytesSizeFnAdapter::RegisterGlobalOverload( - cel::builtin::kSize, bytes_size_func, registry)); - - return BytesSizeFnAdapter::RegisterMemberOverload(cel::builtin::kSize, - bytes_size_func, registry); + for (bool receiver_style : {true, false}) { + for (Kind kind : {Kind::kString, Kind::kBytes}) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(cel::builtin::kSize, receiver_style, {kind}), + std::make_unique(StringFunction::Op::kSize))); + } + } + return absl::OkStatus(); } absl::Status RegisterConcatFunctions(FunctionRegistry& registry) { - using StrCatFnAdapter = - BinaryFunctionAdapter, const StringValue&, - const StringValue&>; - CEL_RETURN_IF_ERROR(StrCatFnAdapter::RegisterGlobalOverload( - cel::builtin::kAdd, &ConcatString, registry)); - - using BytesCatFnAdapter = - BinaryFunctionAdapter, const BytesValue&, - const BytesValue&>; - return BytesCatFnAdapter::RegisterGlobalOverload(cel::builtin::kAdd, - &ConcatBytes, registry); + for (Kind kind : {Kind::kString, Kind::kBytes}) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(cel::builtin::kAdd, /*is_receiver_style=*/false, + {kind, kind}), + std::make_unique(StringFunction::Op::kConcat))); + } + return absl::OkStatus(); } } // namespace @@ -109,24 +124,21 @@ absl::Status RegisterConcatFunctions(FunctionRegistry& registry) { absl::Status RegisterStringFunctions(FunctionRegistry& registry, const RuntimeOptions& options) { // Basic substring tests (contains, startsWith, endsWith) + static constexpr struct { + absl::string_view name; + StringFunction::Op op; + } kSubstringOps[] = { + {cel::builtin::kStringContains, StringFunction::Op::kContains}, + {cel::builtin::kStringEndsWith, StringFunction::Op::kEndsWith}, + {cel::builtin::kStringStartsWith, StringFunction::Op::kStartsWith}, + }; for (bool receiver_style : {true, false}) { - auto status = - BinaryFunctionAdapter:: - Register(cel::builtin::kStringContains, receiver_style, - StringContains, registry); - CEL_RETURN_IF_ERROR(status); - - status = - BinaryFunctionAdapter:: - Register(cel::builtin::kStringEndsWith, receiver_style, - StringEndsWith, registry); - CEL_RETURN_IF_ERROR(status); - - status = - BinaryFunctionAdapter:: - Register(cel::builtin::kStringStartsWith, receiver_style, - StringStartsWith, registry); - CEL_RETURN_IF_ERROR(status); + for (const auto& entry : kSubstringOps) { + CEL_RETURN_IF_ERROR( + registry.Register(FunctionDescriptor(entry.name, receiver_style, + {Kind::kString, Kind::kString}), + std::make_unique(entry.op))); + } } // string concatenation if enabled diff --git a/runtime/standard/time_functions.cc b/runtime/standard/time_functions.cc index d517e6f40..e9feae711 100644 --- a/runtime/standard/time_functions.cc +++ b/runtime/standard/time_functions.cc @@ -15,25 +15,28 @@ #include "runtime/standard/time_functions.h" #include -#include +#include #include #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/match.h" +#include "absl/strings/str_cat.h" #include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" #include "absl/time/civil_time.h" #include "absl/time/time.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" +#include "common/value_kind.h" #include "internal/overflow.h" #include "internal/status_macros.h" #include "runtime/function.h" #include "runtime/function_registry.h" #include "runtime/runtime_options.h" -#include "google/protobuf/arena.h" namespace cel { namespace { @@ -72,480 +75,286 @@ absl::Status FindTimeBreakdown(absl::Time timestamp, absl::string_view tz, return absl::InvalidArgumentError("Invalid timezone"); } -Value GetTimeBreakdownPart( - absl::Time timestamp, absl::string_view tz, - const std::function& - extractor_func, - google::protobuf::Arena* arena) { - absl::TimeZone::CivilInfo breakdown; - auto status = FindTimeBreakdown(timestamp, tz, &breakdown); - - if (!status.ok()) { - return ErrorValue::From(status, arena); - } - - return IntValue(extractor_func(breakdown)); -} - -Value GetFullYear(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { - return breakdown.cs.year(); - }, - context.arena()); -} - -Value GetMonth(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { - return breakdown.cs.month() - 1; - }, - context.arena()); -} - -Value GetDayOfYear(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { - return absl::GetYearDay(absl::CivilDay(breakdown.cs)) - 1; - }, - context.arena()); -} +class TimeAccessorFunction final : public Function { + public: + enum class Field { + kFullYear, + kMonth, + kDayOfYear, + kDayOfMonth, + kDate, + kDayOfWeek, + kHours, + kMinutes, + kSeconds, + kMilliseconds, + }; + + explicit TimeAccessorFunction(Field field) : field_(field) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.empty() || args.size() > 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for time accessor"); + } + if (args[0].IsDuration()) { + if (args.size() != 1) { + return absl::InvalidArgumentError( + "unexpected number of arguments for duration accessor"); + } + absl::Duration d = args[0].GetDuration().ToDuration(); + switch (field_) { + case Field::kHours: + return IntValue(absl::ToInt64Hours(d)); + case Field::kMinutes: + return IntValue(absl::ToInt64Minutes(d)); + case Field::kSeconds: + return IntValue(absl::ToInt64Seconds(d)); + case Field::kMilliseconds: { + constexpr int64_t millis_per_second = 1000L; + return IntValue(absl::ToInt64Milliseconds(d) % millis_per_second); + } + default: + return absl::InvalidArgumentError( + "unsupported accessor field for duration"); + } + } -Value GetDayOfMonth(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { - return breakdown.cs.day() - 1; - }, - context.arena()); -} + if (!args[0].IsTimestamp()) { + return absl::InvalidArgumentError("expected timestamp or duration value"); + } + std::string tz; + if (args.size() == 2) { + if (!args[1].IsString()) { + return absl::InvalidArgumentError("expected string timezone"); + } + tz = args[1].GetString().ToString(); + } -Value GetDate(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { - return breakdown.cs.day(); - }, - context.arena()); -} + absl::TimeZone::CivilInfo breakdown; + absl::Status status = + FindTimeBreakdown(args[0].GetTimestamp().ToTime(), tz, &breakdown); + if (!status.ok()) { + return ErrorValue::From(status, context.arena()); + } -Value GetDayOfWeek(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { + switch (field_) { + case Field::kFullYear: + return IntValue(breakdown.cs.year()); + case Field::kMonth: + return IntValue(breakdown.cs.month() - 1); + case Field::kDayOfYear: + return IntValue(absl::GetYearDay(absl::CivilDay(breakdown.cs)) - 1); + case Field::kDayOfMonth: + return IntValue(breakdown.cs.day() - 1); + case Field::kDate: + return IntValue(breakdown.cs.day()); + case Field::kDayOfWeek: { absl::Weekday weekday = absl::GetWeekday(breakdown.cs); - // get day of week from the date in UTC, zero-based, zero for Sunday, // based on GetDayOfWeek CEL function definition. int weekday_num = static_cast(weekday); weekday_num = (weekday_num == 6) ? 0 : weekday_num + 1; - return weekday_num; - }, - context.arena()); -} - -Value GetHours(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { - return breakdown.cs.hour(); - }, - context.arena()); -} - -Value GetMinutes(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { - return breakdown.cs.minute(); - }, - context.arena()); -} - -Value GetSeconds(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { - return breakdown.cs.second(); - }, - context.arena()); -} - -Value GetMilliseconds(absl::Time timestamp, absl::string_view tz, - const Function::InvokeContext& context) { - return GetTimeBreakdownPart( - timestamp, tz, - [](const absl::TimeZone::CivilInfo& breakdown) { - return absl::ToInt64Milliseconds(breakdown.subsecond); - }, - context.arena()); -} - -absl::Status RegisterTimestampFunctions(FunctionRegistry& registry, - const RuntimeOptions& options) { - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kFullYear, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetFullYear(ts, tz.ToString(), context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor( - builtin::kFullYear, true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetFullYear(ts, "", context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kMonth, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetMonth(ts, tz.ToString(), context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor(builtin::kMonth, - true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetMonth(ts, "", context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kDayOfYear, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetDayOfYear(ts, tz.ToString(), context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor( - builtin::kDayOfYear, true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetDayOfYear(ts, "", context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kDayOfMonth, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetDayOfMonth(ts, tz.ToString(), context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor( - builtin::kDayOfMonth, true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetDayOfMonth(ts, "", context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kDate, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetDate(ts, tz.ToString(), context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor(builtin::kDate, - true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetDate(ts, "", context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kDayOfWeek, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetDayOfWeek(ts, tz.ToString(), context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor( - builtin::kDayOfWeek, true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetDayOfWeek(ts, "", context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kHours, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetHours(ts, tz.ToString(), context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor(builtin::kHours, - true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetHours(ts, "", context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kMinutes, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetMinutes(ts, tz.ToString(), context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor( - builtin::kMinutes, true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetMinutes(ts, "", context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kSeconds, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetSeconds(ts, tz.ToString(), context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - UnaryFunctionAdapter::CreateDescriptor( - builtin::kSeconds, true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetSeconds(ts, "", context); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kMilliseconds, true), - BinaryFunctionAdapter:: - WrapFunction([](absl::Time ts, const StringValue& tz, - const Function::InvokeContext& context) -> Value { - return GetMilliseconds(ts, tz.ToString(), context); - }))); - - return registry.Register( - UnaryFunctionAdapter::CreateDescriptor( - builtin::kMilliseconds, true), - UnaryFunctionAdapter::WrapFunction( - [](absl::Time ts, const Function::InvokeContext& context) -> Value { - return GetMilliseconds(ts, "", context); - })); -} + return IntValue(weekday_num); + } + case Field::kHours: + return IntValue(breakdown.cs.hour()); + case Field::kMinutes: + return IntValue(breakdown.cs.minute()); + case Field::kSeconds: + return IntValue(breakdown.cs.second()); + case Field::kMilliseconds: + return IntValue(absl::ToInt64Milliseconds(breakdown.subsecond)); + } + } -absl::Status RegisterCheckedTimeArithmeticFunctions( - FunctionRegistry& registry) { - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter::CreateDescriptor(builtin::kAdd, - false), - BinaryFunctionAdapter, absl::Time, absl::Duration>:: - WrapFunction([](absl::Time t1, absl::Duration d2, - const Function::InvokeContext& context) - -> absl::StatusOr { - auto sum = cel::internal::CheckedAdd(t1, d2); - if (!sum.ok()) { - return ErrorValue::From(sum.status(), context.arena()); - } - return TimestampValue(*sum); - }))); + private: + Field field_; +}; - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter, absl::Duration, - absl::Time>::CreateDescriptor(builtin::kAdd, false), - BinaryFunctionAdapter, absl::Duration, absl::Time>:: - WrapFunction([](absl::Duration d2, absl::Time t1, - const Function::InvokeContext& context) - -> absl::StatusOr { - auto sum = cel::internal::CheckedAdd(t1, d2); - if (!sum.ok()) { - return ErrorValue::From(sum.status(), context.arena()); - } - return TimestampValue(*sum); - }))); +class TimeArithmeticFunction final : public Function { + public: + enum class Op { kAdd, kSubtract }; - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter, absl::Duration, - absl::Duration>::CreateDescriptor(builtin::kAdd, - false), - BinaryFunctionAdapter, absl::Duration, - absl::Duration>:: - WrapFunction([](absl::Duration d1, absl::Duration d2, - const Function::InvokeContext& context) - -> absl::StatusOr { - auto sum = cel::internal::CheckedAdd(d1, d2); - if (!sum.ok()) { - return ErrorValue::From(sum.status(), context.arena()); - } - return DurationValue(*sum); - }))); + TimeArithmeticFunction(Op op, bool checked) : op_(op), checked_(checked) {} - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter, absl::Time, absl::Duration>:: - CreateDescriptor(builtin::kSubtract, false), - BinaryFunctionAdapter, absl::Time, absl::Duration>:: - WrapFunction([](absl::Time t1, absl::Duration d2, - const Function::InvokeContext& context) - -> absl::StatusOr { - auto diff = cel::internal::CheckedSub(t1, d2); - if (!diff.ok()) { - return ErrorValue::From(diff.status(), context.arena()); - } - return TimestampValue(*diff); - }))); + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.size() != 2) { + return absl::InvalidArgumentError( + "unexpected number of arguments for binary function"); + } + ValueKind lhs_kind = args[0].kind(); + ValueKind rhs_kind = args[1].kind(); + if (op_ == Op::kAdd) { + if (lhs_kind == ValueKind::kTimestamp && + rhs_kind == ValueKind::kDuration) { + absl::Time t1 = args[0].GetTimestamp().ToTime(); + absl::Duration d2 = args[1].GetDuration().ToDuration(); + if (!checked_) { + return UnsafeTimestampValue(t1 + d2); + } + auto sum = cel::internal::CheckedAdd(t1, d2); + if (!sum.ok()) { + return ErrorValue::From(sum.status(), context.arena()); + } + return TimestampValue(*sum); + } + if (lhs_kind == ValueKind::kDuration && + rhs_kind == ValueKind::kTimestamp) { + absl::Duration d2 = args[0].GetDuration().ToDuration(); + absl::Time t1 = args[1].GetTimestamp().ToTime(); + if (!checked_) { + return UnsafeTimestampValue(t1 + d2); + } + auto sum = cel::internal::CheckedAdd(t1, d2); + if (!sum.ok()) { + return ErrorValue::From(sum.status(), context.arena()); + } + return TimestampValue(*sum); + } + if (lhs_kind == ValueKind::kDuration && + rhs_kind == ValueKind::kDuration) { + absl::Duration d1 = args[0].GetDuration().ToDuration(); + absl::Duration d2 = args[1].GetDuration().ToDuration(); + if (!checked_) { + return UnsafeDurationValue(d1 + d2); + } + auto sum = cel::internal::CheckedAdd(d1, d2); + if (!sum.ok()) { + return ErrorValue::From(sum.status(), context.arena()); + } + return DurationValue(*sum); + } + return absl::InvalidArgumentError( + "expected timestamp/duration arguments for add"); + } - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter, absl::Time, - absl::Time>::CreateDescriptor(builtin::kSubtract, - false), - BinaryFunctionAdapter, absl::Time, absl::Time>:: - WrapFunction([](absl::Time t1, absl::Time t2, - const Function::InvokeContext& context) - -> absl::StatusOr { - auto diff = cel::internal::CheckedSub(t1, t2); - if (!diff.ok()) { - return ErrorValue::From(diff.status(), context.arena()); - } - return DurationValue(*diff); - }))); + // Op::kSubtract + if (lhs_kind == ValueKind::kTimestamp && rhs_kind == ValueKind::kDuration) { + absl::Time t1 = args[0].GetTimestamp().ToTime(); + absl::Duration d2 = args[1].GetDuration().ToDuration(); + if (!checked_) { + return UnsafeTimestampValue(t1 - d2); + } + auto diff = cel::internal::CheckedSub(t1, d2); + if (!diff.ok()) { + return ErrorValue::From(diff.status(), context.arena()); + } + return TimestampValue(*diff); + } + if (lhs_kind == ValueKind::kTimestamp && + rhs_kind == ValueKind::kTimestamp) { + absl::Time t1 = args[0].GetTimestamp().ToTime(); + absl::Time t2 = args[1].GetTimestamp().ToTime(); + if (!checked_) { + return UnsafeDurationValue(t1 - t2); + } + auto diff = cel::internal::CheckedSub(t1, t2); + if (!diff.ok()) { + return ErrorValue::From(diff.status(), context.arena()); + } + return DurationValue(*diff); + } + if (lhs_kind == ValueKind::kDuration && rhs_kind == ValueKind::kDuration) { + absl::Duration d1 = args[0].GetDuration().ToDuration(); + absl::Duration d2 = args[1].GetDuration().ToDuration(); + if (!checked_) { + return UnsafeDurationValue(d1 - d2); + } + auto diff = cel::internal::CheckedSub(d1, d2); + if (!diff.ok()) { + return ErrorValue::From(diff.status(), context.arena()); + } + return DurationValue(*diff); + } + return absl::InvalidArgumentError( + "expected timestamp/duration arguments for subtract"); + } - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter< - absl::StatusOr, absl::Duration, - absl::Duration>::CreateDescriptor(builtin::kSubtract, false), - BinaryFunctionAdapter, absl::Duration, - absl::Duration>:: - WrapFunction([](absl::Duration d1, absl::Duration d2, - const Function::InvokeContext& context) - -> absl::StatusOr { - auto diff = cel::internal::CheckedSub(d1, d2); - if (!diff.ok()) { - return ErrorValue::From(diff.status(), context.arena()); - } - return DurationValue(*diff); - }))); + private: + Op op_; + bool checked_; +}; +absl::Status RegisterTimestampFunctions(FunctionRegistry& registry, + const RuntimeOptions& options) { + static constexpr struct { + absl::string_view name; + TimeAccessorFunction::Field field; + } kTimestampAccessors[] = { + {builtin::kFullYear, TimeAccessorFunction::Field::kFullYear}, + {builtin::kMonth, TimeAccessorFunction::Field::kMonth}, + {builtin::kDayOfYear, TimeAccessorFunction::Field::kDayOfYear}, + {builtin::kDayOfMonth, TimeAccessorFunction::Field::kDayOfMonth}, + {builtin::kDate, TimeAccessorFunction::Field::kDate}, + {builtin::kDayOfWeek, TimeAccessorFunction::Field::kDayOfWeek}, + {builtin::kHours, TimeAccessorFunction::Field::kHours}, + {builtin::kMinutes, TimeAccessorFunction::Field::kMinutes}, + {builtin::kSeconds, TimeAccessorFunction::Field::kSeconds}, + {builtin::kMilliseconds, TimeAccessorFunction::Field::kMilliseconds}, + }; + for (const auto& entry : kTimestampAccessors) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(entry.name, /*is_receiver_style=*/true, + {Kind::kTimestamp, Kind::kString}), + std::make_unique(entry.field))); + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(entry.name, /*is_receiver_style=*/true, + {Kind::kTimestamp}), + std::make_unique(entry.field))); + } return absl::OkStatus(); } -absl::Status RegisterUncheckedTimeArithmeticFunctions( - FunctionRegistry& registry) { - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter::CreateDescriptor(builtin::kAdd, - false), - BinaryFunctionAdapter::WrapFunction( - [](absl::Time t1, absl::Duration d2) -> Value { - return UnsafeTimestampValue(t1 + d2); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter::CreateDescriptor(builtin::kAdd, false), - BinaryFunctionAdapter::WrapFunction( - [](absl::Duration d2, absl::Time t1) -> Value { - return UnsafeTimestampValue(t1 + d2); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter::CreateDescriptor(builtin::kAdd, - false), - BinaryFunctionAdapter:: - WrapFunction([](absl::Duration d1, absl::Duration d2) -> Value { - return UnsafeDurationValue(d1 + d2); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kSubtract, false), - - BinaryFunctionAdapter::WrapFunction( - - [](absl::Time t1, absl::Duration d2) -> Value { - return UnsafeTimestampValue(t1 - d2); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter::CreateDescriptor( - builtin::kSubtract, false), - BinaryFunctionAdapter::WrapFunction( - - [](absl::Time t1, absl::Time t2) -> Value { - return UnsafeDurationValue(t1 - t2); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - BinaryFunctionAdapter:: - CreateDescriptor(builtin::kSubtract, false), - BinaryFunctionAdapter:: - WrapFunction([](absl::Duration d1, absl::Duration d2) -> Value { - return UnsafeDurationValue(d1 - d2); - }))); - +absl::Status RegisterTimeArithmeticFunctions(FunctionRegistry& registry, + bool checked) { + static constexpr struct { + absl::string_view name; + TimeArithmeticFunction::Op op; + Kind lhs; + Kind rhs; + } kOverloads[] = { + {builtin::kAdd, TimeArithmeticFunction::Op::kAdd, Kind::kTimestamp, + Kind::kDuration}, + {builtin::kAdd, TimeArithmeticFunction::Op::kAdd, Kind::kDuration, + Kind::kTimestamp}, + {builtin::kAdd, TimeArithmeticFunction::Op::kAdd, Kind::kDuration, + Kind::kDuration}, + {builtin::kSubtract, TimeArithmeticFunction::Op::kSubtract, + Kind::kTimestamp, Kind::kDuration}, + {builtin::kSubtract, TimeArithmeticFunction::Op::kSubtract, + Kind::kTimestamp, Kind::kTimestamp}, + {builtin::kSubtract, TimeArithmeticFunction::Op::kSubtract, + Kind::kDuration, Kind::kDuration}, + }; + for (const auto& entry : kOverloads) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(entry.name, /*is_receiver_style=*/false, + {entry.lhs, entry.rhs}), + std::make_unique(entry.op, checked))); + } return absl::OkStatus(); } absl::Status RegisterDurationFunctions(FunctionRegistry& registry) { - // duration breakdown accessor functions - using DurationAccessorFunction = - UnaryFunctionAdapter; - CEL_RETURN_IF_ERROR(registry.Register( - DurationAccessorFunction::CreateDescriptor(builtin::kHours, true), - DurationAccessorFunction::WrapFunction( - [](absl::Duration d) -> int64_t { return absl::ToInt64Hours(d); }))); - - CEL_RETURN_IF_ERROR(registry.Register( - DurationAccessorFunction::CreateDescriptor(builtin::kMinutes, true), - DurationAccessorFunction::WrapFunction([](absl::Duration d) -> int64_t { - return absl::ToInt64Minutes(d); - }))); - - CEL_RETURN_IF_ERROR(registry.Register( - DurationAccessorFunction::CreateDescriptor(builtin::kSeconds, true), - DurationAccessorFunction::WrapFunction([](absl::Duration d) -> int64_t { - return absl::ToInt64Seconds(d); - }))); - - return registry.Register( - DurationAccessorFunction::CreateDescriptor(builtin::kMilliseconds, true), - DurationAccessorFunction::WrapFunction([](absl::Duration d) -> int64_t { - constexpr int64_t millis_per_second = 1000L; - return absl::ToInt64Milliseconds(d) % millis_per_second; - })); + static constexpr struct { + absl::string_view name; + TimeAccessorFunction::Field field; + } kDurationAccessors[] = { + {builtin::kHours, TimeAccessorFunction::Field::kHours}, + {builtin::kMinutes, TimeAccessorFunction::Field::kMinutes}, + {builtin::kSeconds, TimeAccessorFunction::Field::kSeconds}, + {builtin::kMilliseconds, TimeAccessorFunction::Field::kMilliseconds}, + }; + for (const auto& entry : kDurationAccessors) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(entry.name, /*is_receiver_style=*/true, + {Kind::kDuration}), + std::make_unique(entry.field))); + } + return absl::OkStatus(); } } // namespace @@ -558,11 +367,8 @@ absl::Status RegisterTimeFunctions(FunctionRegistry& registry, // Special arithmetic operators for Timestamp and Duration // TODO(uncreated-issue/37): deprecate unchecked time math functions when clients no // longer depend on them. - if (options.enable_timestamp_duration_overflow_errors) { - return RegisterCheckedTimeArithmeticFunctions(registry); - } - - return RegisterUncheckedTimeArithmeticFunctions(registry); + return RegisterTimeArithmeticFunctions( + registry, options.enable_timestamp_duration_overflow_errors); } } // namespace cel diff --git a/runtime/standard/type_conversion_functions.cc b/runtime/standard/type_conversion_functions.cc index 2c5130aa3..9302e6e0c 100644 --- a/runtime/standard/type_conversion_functions.cc +++ b/runtime/standard/type_conversion_functions.cc @@ -16,6 +16,7 @@ #include #include +#include #include // NOLINT (required for std::to_chars_result) #include @@ -25,9 +26,12 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" +#include "absl/types/span.h" #include "base/builtins.h" -#include "base/function_adapter.h" +#include "common/function_descriptor.h" +#include "common/kind.h" #include "common/value.h" +#include "common/value_kind.h" #include "internal/overflow.h" #include "internal/status_macros.h" #include "internal/time.h" @@ -80,417 +84,401 @@ Value LegacyFormatDouble(double v, const Function::InvokeContext& context) { return StringValue::From(absl::StrCat(v), context.arena()); } -absl::Status RegisterBoolConversionFunctions(FunctionRegistry& registry, - const RuntimeOptions&) { - // bool -> bool - absl::Status status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kBool, [](bool v) { return v; }, registry); - CEL_RETURN_IF_ERROR(status); - - // string -> bool - return UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kBool, - [](const StringValue& v, - const Function::InvokeContext& context) -> Value { - if ((v == "true") || (v == "True") || (v == "TRUE") || (v == "t") || - (v == "1")) { - return TrueValue(); - } else if ((v == "false") || (v == "FALSE") || (v == "False") || - (v == "f") || (v == "0")) { - return FalseValue(); - } else { - return ErrorValue::From( - absl::InvalidArgumentError( - "Type conversion error from 'string' to 'bool'"), - context.arena()); - } - }, - registry); +Value ConvertToBool(const Value& arg, const Function::InvokeContext& context) { + switch (arg.kind()) { + case ValueKind::kBool: + return arg; + case ValueKind::kString: { + const StringValue& v = arg.GetString(); + if ((v == "true") || (v == "True") || (v == "TRUE") || (v == "t") || + (v == "1")) { + return TrueValue(); + } + if ((v == "false") || (v == "FALSE") || (v == "False") || (v == "f") || + (v == "0")) { + return FalseValue(); + } + return ErrorValue::From( + absl::InvalidArgumentError( + "Type conversion error from 'string' to 'bool'"), + context.arena()); + } + default: + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert value to bool"), + context.arena()); + } } -absl::Status RegisterIntConversionFunctions(FunctionRegistry& registry, - const RuntimeOptions&) { - // bool -> int - absl::Status status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kInt, [](bool v) { return static_cast(v); }, - registry); - CEL_RETURN_IF_ERROR(status); - - // double -> int - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kInt, - [](double v, const Function::InvokeContext& context) -> Value { - auto conv = cel::internal::CheckedDoubleToInt64(v); - if (!conv.ok()) { - return ErrorValue::From(conv.status(), context.arena()); - } - return IntValue(*conv); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // int -> int - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kInt, [](int64_t v) { return v; }, registry); - CEL_RETURN_IF_ERROR(status); - - // string -> int - status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kInt, - [](const StringValue& s, - const Function::InvokeContext& context) -> Value { - int64_t result; - if (!absl::SimpleAtoi(s.ToString(), &result)) { - return ErrorValue::From( - absl::InvalidArgumentError("cannot convert string to int"), - context.arena()); - } - return IntValue(result); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // time -> int - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kInt, [](absl::Time t) { return absl::ToUnixSeconds(t); }, - registry); - CEL_RETURN_IF_ERROR(status); - - // uint -> int - return UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kInt, - [](uint64_t v, const Function::InvokeContext& context) -> Value { - auto conv = cel::internal::CheckedUint64ToInt64(v); - if (!conv.ok()) { - return ErrorValue::From(conv.status(), context.arena()); - } - return IntValue(*conv); - }, - registry); +Value ConvertToInt(const Value& arg, const Function::InvokeContext& context) { + switch (arg.kind()) { + case ValueKind::kInt: + return arg; + case ValueKind::kBool: + return IntValue(static_cast(arg.GetBool().NativeValue())); + case ValueKind::kDouble: { + auto conv = + cel::internal::CheckedDoubleToInt64(arg.GetDouble().NativeValue()); + if (!conv.ok()) { + return ErrorValue::From(conv.status(), context.arena()); + } + return IntValue(*conv); + } + case ValueKind::kString: { + int64_t result; + if (!absl::SimpleAtoi(arg.GetString().ToString(), &result)) { + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert string to int"), + context.arena()); + } + return IntValue(result); + } + case ValueKind::kTimestamp: + return IntValue(absl::ToUnixSeconds(arg.GetTimestamp().ToTime())); + case ValueKind::kUint: { + auto conv = + cel::internal::CheckedUint64ToInt64(arg.GetUint().NativeValue()); + if (!conv.ok()) { + return ErrorValue::From(conv.status(), context.arena()); + } + return IntValue(*conv); + } + default: + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert value to int"), + context.arena()); + } } -absl::Status RegisterStringConversionFunctions(FunctionRegistry& registry, - const RuntimeOptions& options) { - // May be optionally disabled to reduce potential allocs. - if (!options.enable_string_conversion) { - return absl::OkStatus(); +Value ConvertToUint(const Value& arg, const Function::InvokeContext& context) { + switch (arg.kind()) { + case ValueKind::kUint: + return arg; + case ValueKind::kDouble: { + auto conv = + cel::internal::CheckedDoubleToUint64(arg.GetDouble().NativeValue()); + if (!conv.ok()) { + return ErrorValue::From(conv.status(), context.arena()); + } + return UintValue(*conv); + } + case ValueKind::kInt: { + auto conv = + cel::internal::CheckedInt64ToUint64(arg.GetInt().NativeValue()); + if (!conv.ok()) { + return ErrorValue::From(conv.status(), context.arena()); + } + return UintValue(*conv); + } + case ValueKind::kString: { + uint64_t result; + if (!absl::SimpleAtoi(arg.GetString().ToString(), &result)) { + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert string to uint"), + context.arena()); + } + return UintValue(result); + } + default: + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert value to uint"), + context.arena()); } - - absl::Status status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kString, - - [](const BytesValue& value, - const Function::InvokeContext& context) -> Value { - auto valid = value.NativeValue([](const auto& value) -> bool { - return internal::Utf8IsValid(value); - }); - if (!valid) { - return ErrorValue::From( - absl::InvalidArgumentError("malformed UTF-8 bytes"), - context.arena()); - } - return StringValue(value); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // bool -> string - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kString, - [](bool value) -> StringValue { - return StringValue::WrapUnsafe(value ? "true" : "false"); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // double -> string - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kString, - (options.enable_precision_preserving_double_format ? &FormatDouble - : &LegacyFormatDouble), - registry); - CEL_RETURN_IF_ERROR(status); - - // int -> string - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kString, - [](int64_t value, const Function::InvokeContext& context) -> StringValue { - return StringValue::From(absl::StrCat(value), context.arena()); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // string -> string - status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kString, - [](StringValue value) -> StringValue { return value; }, registry); - CEL_RETURN_IF_ERROR(status); - - // uint -> string - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kString, - [](uint64_t value, - const Function::InvokeContext& context) -> StringValue { - return StringValue::From(absl::StrCat(value), context.arena()); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // duration -> string - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kString, - [](absl::Duration value, - const Function::InvokeContext& context) -> Value { - auto encode = EncodeDurationToJson(value); - if (!encode.ok()) { - return ErrorValue::From(encode.status(), context.arena()); - } - return StringValue::From(*encode, context.arena()); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // timestamp -> string - return UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kString, - [](absl::Time value, const Function::InvokeContext& context) -> Value { - auto encode = EncodeTimestampToJson(value); - if (!encode.ok()) { - return ErrorValue::From(encode.status(), context.arena()); - } - return StringValue::From(*encode, context.arena()); - }, - registry); } -absl::Status RegisterUintConversionFunctions(FunctionRegistry& registry, - const RuntimeOptions&) { - // double -> uint - absl::Status status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kUint, - [](double v, const Function::InvokeContext& context) -> Value { - auto conv = cel::internal::CheckedDoubleToUint64(v); - if (!conv.ok()) { - return ErrorValue::From(conv.status(), context.arena()); - } - return UintValue(*conv); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // int -> uint - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kUint, - [](int64_t v, const Function::InvokeContext& context) -> Value { - auto conv = cel::internal::CheckedInt64ToUint64(v); - if (!conv.ok()) { - return ErrorValue::From(conv.status(), context.arena()); - } - return UintValue(*conv); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // string -> uint - status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kUint, - [](const StringValue& s, - const Function::InvokeContext& context) -> Value { - uint64_t result; - if (!absl::SimpleAtoi(s.ToString(), &result)) { - return ErrorValue::From( - absl::InvalidArgumentError("cannot convert string to uint"), - context.arena()); - } - return UintValue(result); - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // uint -> uint - return UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kUint, [](uint64_t v) { return v; }, registry); +Value ConvertToDouble(const Value& arg, + const Function::InvokeContext& context) { + switch (arg.kind()) { + case ValueKind::kDouble: + return arg; + case ValueKind::kInt: + return DoubleValue(static_cast(arg.GetInt().NativeValue())); + case ValueKind::kUint: + return DoubleValue(static_cast(arg.GetUint().NativeValue())); + case ValueKind::kString: { + double result; + if (absl::SimpleAtod(arg.GetString().ToString(), &result)) { + return DoubleValue(result); + } + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert string to double"), + context.arena()); + } + default: + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert value to double"), + context.arena()); + } } -absl::Status RegisterBytesConversionFunctions(FunctionRegistry& registry, - const RuntimeOptions&) { - // bytes -> bytes - absl::Status status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kBytes, - - [](BytesValue value) -> BytesValue { return value; }, registry); - CEL_RETURN_IF_ERROR(status); - - // string -> bytes - return UnaryFunctionAdapter, const StringValue&>:: - RegisterGlobalOverload( - cel::builtin::kBytes, - [](const StringValue& value) { return BytesValue(value); }, registry); +Value ConvertToString(const Value& arg, bool precision_preserving_double_format, + const Function::InvokeContext& context) { + switch (arg.kind()) { + case ValueKind::kString: + return arg; + case ValueKind::kBytes: { + const BytesValue& value = arg.GetBytes(); + auto valid = value.NativeValue([](const auto& bytes) -> bool { + return internal::Utf8IsValid(bytes); + }); + if (!valid) { + return ErrorValue::From( + absl::InvalidArgumentError("malformed UTF-8 bytes"), + context.arena()); + } + return StringValue(value); + } + case ValueKind::kBool: + return StringValue::WrapUnsafe(arg.GetBool().NativeValue() ? "true" + : "false"); + case ValueKind::kDouble: + return precision_preserving_double_format + ? FormatDouble(arg.GetDouble().NativeValue(), context) + : LegacyFormatDouble(arg.GetDouble().NativeValue(), context); + case ValueKind::kInt: + return StringValue::From(absl::StrCat(arg.GetInt().NativeValue()), + context.arena()); + case ValueKind::kUint: + return StringValue::From(absl::StrCat(arg.GetUint().NativeValue()), + context.arena()); + case ValueKind::kDuration: { + auto encode = EncodeDurationToJson(arg.GetDuration().ToDuration()); + if (!encode.ok()) { + return ErrorValue::From(encode.status(), context.arena()); + } + return StringValue::From(*encode, context.arena()); + } + case ValueKind::kTimestamp: { + auto encode = EncodeTimestampToJson(arg.GetTimestamp().ToTime()); + if (!encode.ok()) { + return ErrorValue::From(encode.status(), context.arena()); + } + return StringValue::From(*encode, context.arena()); + } + default: + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert value to string"), + context.arena()); + } } -absl::Status RegisterDoubleConversionFunctions(FunctionRegistry& registry, - const RuntimeOptions&) { - // double -> double - absl::Status status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kDouble, [](double v) { return v; }, registry); - CEL_RETURN_IF_ERROR(status); - - // int -> double - status = UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kDouble, [](int64_t v) { return static_cast(v); }, - registry); - CEL_RETURN_IF_ERROR(status); - - // string -> double - status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kDouble, - [](const StringValue& s, - const Function::InvokeContext& context) -> Value { - double result; - if (absl::SimpleAtod(s.ToString(), &result)) { - return DoubleValue(result); - } else { - return ErrorValue::From( - absl::InvalidArgumentError("cannot convert string to double"), - context.arena()); - } - }, - registry); - CEL_RETURN_IF_ERROR(status); - - // uint -> double - return UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kDouble, [](uint64_t v) { return static_cast(v); }, - registry); +Value ConvertToDuration(const Value& arg, + const Function::InvokeContext& context) { + switch (arg.kind()) { + case ValueKind::kDuration: + return UnsafeDurationValue(arg.GetDuration().ToDuration()); + case ValueKind::kString: { + absl::Duration d; + if (!absl::ParseDuration(arg.GetString().ToString(), &d)) { + return ErrorValue::From( + absl::InvalidArgumentError("String to Duration conversion failed"), + context.arena()); + } + auto status = internal::ValidateDuration(d); + if (!status.ok()) { + return ErrorValue::From(std::move(status), context.arena()); + } + return DurationValue(d); + } + default: + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert value to duration"), + context.arena()); + } } -Value CreateDurationFromString(const StringValue& dur_str, - const Function::InvokeContext& context) { - absl::Duration d; - if (!absl::ParseDuration(dur_str.ToString(), &d)) { - return ErrorValue::From( - absl::InvalidArgumentError("String to Duration conversion failed"), - context.arena()); +Value ConvertToTimestamp(const Value& arg, + bool enable_timestamp_duration_overflow_errors, + const Function::InvokeContext& context) { + switch (arg.kind()) { + case ValueKind::kTimestamp: + return UnsafeTimestampValue(arg.GetTimestamp().ToTime()); + case ValueKind::kInt: { + absl::Time ts = absl::FromUnixSeconds(arg.GetInt().NativeValue()); + if (enable_timestamp_duration_overflow_errors) { + if (ts < MinTimestamp() || ts > MaxTimestamp()) { + return ErrorValue::From(absl::OutOfRangeError("timestamp overflow"), + context.arena()); + } + } + return UnsafeTimestampValue(ts); + } + case ValueKind::kString: { + absl::Time ts; + if (!absl::ParseTime(absl::RFC3339_full, arg.GetString().ToString(), &ts, + nullptr)) { + return ErrorValue::From( + absl::InvalidArgumentError("String to Timestamp conversion failed"), + context.arena()); + } + if (enable_timestamp_duration_overflow_errors) { + if (ts < MinTimestamp() || ts > MaxTimestamp()) { + return ErrorValue::From(absl::OutOfRangeError("timestamp overflow"), + context.arena()); + } + } + return UnsafeTimestampValue(ts); + } + default: + return ErrorValue::From( + absl::InvalidArgumentError("cannot convert value to timestamp"), + context.arena()); } +} - auto status = internal::ValidateDuration(d); - if (!status.ok()) { - return ErrorValue::From(std::move(status), context.arena()); +class TypeConversionFunction final : public Function { + public: + enum class Target { + kBool, + kInt, + kUint, + kDouble, + kString, + kBytes, + kDuration, + kTimestamp, + kDyn, + kType, + }; + + TypeConversionFunction(Target target, + bool precision_preserving_double_format = true, + bool enable_timestamp_duration_overflow_errors = false) + : target_(target), + precision_preserving_double_format_(precision_preserving_double_format), + enable_timestamp_duration_overflow_errors_( + enable_timestamp_duration_overflow_errors) {} + + absl::StatusOr Invoke(absl::Span args, + const InvokeContext& context) const override { + if (args.size() != 1) { + return absl::InvalidArgumentError( + "unexpected number of arguments for unary function"); + } + const Value& arg = args[0]; + switch (target_) { + case Target::kBool: + return ConvertToBool(arg, context); + case Target::kInt: + return ConvertToInt(arg, context); + case Target::kUint: + return ConvertToUint(arg, context); + case Target::kDouble: + return ConvertToDouble(arg, context); + case Target::kString: + return ConvertToString(arg, precision_preserving_double_format_, + context); + case Target::kBytes: + if (arg.IsBytes()) { + return arg; + } + if (arg.IsString()) { + return BytesValue(arg.GetString()); + } + return absl::InvalidArgumentError("cannot convert value to bytes"); + case Target::kDuration: + return ConvertToDuration(arg, context); + case Target::kTimestamp: + return ConvertToTimestamp( + arg, enable_timestamp_duration_overflow_errors_, context); + case Target::kDyn: + return arg; + case Target::kType: + return TypeValue(arg.GetRuntimeType()); + } } - return DurationValue(d); -} -absl::Status RegisterTimeConversionFunctions(FunctionRegistry& registry, - const RuntimeOptions& options) { - // duration() conversion from string. - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kDuration, CreateDurationFromString, registry))); - - bool enable_timestamp_duration_overflow_errors = - options.enable_timestamp_duration_overflow_errors; - - // timestamp conversion from int. - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kTimestamp, - [=](int64_t epoch_seconds, - const Function::InvokeContext& context) -> Value { - absl::Time ts = absl::FromUnixSeconds(epoch_seconds); - if (enable_timestamp_duration_overflow_errors) { - if (ts < MinTimestamp() || ts > MaxTimestamp()) { - return ErrorValue::From( - absl::OutOfRangeError("timestamp overflow"), - context.arena()); - } - } - return UnsafeTimestampValue(ts); - }, - registry))); - - // timestamp -> timestamp - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kTimestamp, - [](absl::Time value) -> Value { return UnsafeTimestampValue(value); }, - registry))); - - // duration -> duration - CEL_RETURN_IF_ERROR( - (UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kDuration, - [](absl::Duration value) -> Value { - return UnsafeDurationValue(value); - }, - registry))); - - // timestamp() conversion from string. - return UnaryFunctionAdapter:: - RegisterGlobalOverload( - cel::builtin::kTimestamp, - [=](const StringValue& time_str, - const Function::InvokeContext& context) -> Value { - absl::Time ts; - if (!absl::ParseTime(absl::RFC3339_full, time_str.ToString(), &ts, - nullptr)) { - return ErrorValue::From( - absl::InvalidArgumentError( - "String to Timestamp conversion failed"), - context.arena()); - } - if (enable_timestamp_duration_overflow_errors) { - if (ts < MinTimestamp() || ts > MaxTimestamp()) { - return ErrorValue::From( - absl::OutOfRangeError("timestamp overflow"), - context.arena()); - } - } - return UnsafeTimestampValue(ts); - }, - registry); + private: + Target target_; + bool precision_preserving_double_format_; + bool enable_timestamp_duration_overflow_errors_; +}; + +absl::Status RegisterConversionOverloads( + FunctionRegistry& registry, absl::string_view name, + TypeConversionFunction::Target target, absl::Span arg_kinds, + bool precision_preserving_double_format = true, + bool enable_timestamp_duration_overflow_errors = false) { + for (Kind kind : arg_kinds) { + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(name, /*is_receiver_style=*/false, {kind}), + std::make_unique( + target, precision_preserving_double_format, + enable_timestamp_duration_overflow_errors))); + } + return absl::OkStatus(); } } // namespace absl::Status RegisterTypeConversionFunctions(FunctionRegistry& registry, const RuntimeOptions& options) { - CEL_RETURN_IF_ERROR(RegisterBoolConversionFunctions(registry, options)); - - CEL_RETURN_IF_ERROR(RegisterBytesConversionFunctions(registry, options)); - - CEL_RETURN_IF_ERROR(RegisterDoubleConversionFunctions(registry, options)); - - CEL_RETURN_IF_ERROR(RegisterIntConversionFunctions(registry, options)); - - CEL_RETURN_IF_ERROR(RegisterStringConversionFunctions(registry, options)); - - CEL_RETURN_IF_ERROR(RegisterUintConversionFunctions(registry, options)); + static constexpr Kind kBoolKinds[] = {Kind::kBool, Kind::kString}; + CEL_RETURN_IF_ERROR(RegisterConversionOverloads( + registry, cel::builtin::kBool, TypeConversionFunction::Target::kBool, + kBoolKinds)); + + static constexpr Kind kBytesKinds[] = {Kind::kBytes, Kind::kString}; + CEL_RETURN_IF_ERROR(RegisterConversionOverloads( + registry, cel::builtin::kBytes, TypeConversionFunction::Target::kBytes, + kBytesKinds)); + + static constexpr Kind kDoubleKinds[] = {Kind::kDouble, Kind::kInt, + Kind::kString, Kind::kUint}; + CEL_RETURN_IF_ERROR(RegisterConversionOverloads( + registry, cel::builtin::kDouble, TypeConversionFunction::Target::kDouble, + kDoubleKinds)); + + static constexpr Kind kIntKinds[] = {Kind::kBool, Kind::kDouble, + Kind::kInt, Kind::kString, + Kind::kTimestamp, Kind::kUint}; + CEL_RETURN_IF_ERROR(RegisterConversionOverloads( + registry, cel::builtin::kInt, TypeConversionFunction::Target::kInt, + kIntKinds)); + + if (options.enable_string_conversion) { + static constexpr Kind kStringKinds[] = { + Kind::kBytes, Kind::kBool, Kind::kDouble, Kind::kInt, + Kind::kString, Kind::kUint, Kind::kDuration, Kind::kTimestamp}; + CEL_RETURN_IF_ERROR(RegisterConversionOverloads( + registry, cel::builtin::kString, + TypeConversionFunction::Target::kString, kStringKinds, + options.enable_precision_preserving_double_format)); + } - CEL_RETURN_IF_ERROR(RegisterTimeConversionFunctions(registry, options)); + static constexpr Kind kUintKinds[] = {Kind::kDouble, Kind::kInt, + Kind::kString, Kind::kUint}; + CEL_RETURN_IF_ERROR(RegisterConversionOverloads( + registry, cel::builtin::kUint, TypeConversionFunction::Target::kUint, + kUintKinds)); + + static constexpr Kind kDurationKinds[] = {Kind::kString, Kind::kDuration}; + CEL_RETURN_IF_ERROR(RegisterConversionOverloads( + registry, cel::builtin::kDuration, + TypeConversionFunction::Target::kDuration, kDurationKinds)); + + static constexpr Kind kTimestampKinds[] = {Kind::kInt, Kind::kTimestamp, + Kind::kString}; + CEL_RETURN_IF_ERROR(RegisterConversionOverloads( + registry, cel::builtin::kTimestamp, + TypeConversionFunction::Target::kTimestamp, kTimestampKinds, + /*precision_preserving_double_format=*/true, + options.enable_timestamp_duration_overflow_errors)); // dyn() identity function. // TODO(issues/102): strip dyn() function references at type-check time. - absl::Status status = - UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kDyn, [](const Value& value) -> Value { return value; }, - registry); - CEL_RETURN_IF_ERROR(status); + CEL_RETURN_IF_ERROR(registry.Register( + FunctionDescriptor(cel::builtin::kDyn, /*is_receiver_style=*/false, + {Kind::kAny}), + std::make_unique( + TypeConversionFunction::Target::kDyn))); // type(dyn) -> type - return UnaryFunctionAdapter::RegisterGlobalOverload( - cel::builtin::kType, - [](const Value& value) { return TypeValue(value.GetRuntimeType()); }, - registry); + return registry.Register( + FunctionDescriptor(cel::builtin::kType, /*is_receiver_style=*/false, + {Kind::kAny}), + std::make_unique( + TypeConversionFunction::Target::kType)); } } // namespace cel diff --git a/runtime/type_registry.cc b/runtime/type_registry.cc index 58bce8afd..be605deb2 100644 --- a/runtime/type_registry.cc +++ b/runtime/type_registry.cc @@ -25,7 +25,6 @@ #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "common/value.h" -#include "runtime/internal/legacy_runtime_type_provider.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/message.h" @@ -34,8 +33,7 @@ namespace cel { TypeRegistry::TypeRegistry( const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nullable message_factory) - : type_provider_(descriptor_pool), - legacy_type_provider_(descriptor_pool, &type_provider_) { + : type_provider_(descriptor_pool) { RegisterEnum("google.protobuf.NullValue", {{"NULL_VALUE", 0}}); } diff --git a/runtime/type_registry.h b/runtime/type_registry.h index 22f923bd6..a5b40187a 100644 --- a/runtime/type_registry.h +++ b/runtime/type_registry.h @@ -29,7 +29,6 @@ #include "base/type_provider.h" #include "common/type.h" #include "common/value.h" -#include "runtime/internal/legacy_runtime_type_provider.h" #include "runtime/internal/runtime_type_provider.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/message.h" @@ -41,8 +40,6 @@ class TypeRegistry; namespace runtime_internal { const RuntimeTypeProvider& GetRuntimeTypeProvider( const TypeRegistry& type_registry); -const LegacyRuntimeTypeProvider& GetLegacyRuntimeTypeProvider( - const TypeRegistry& type_registry); // Returns a memoized table of fully qualified enum values. // @@ -104,9 +101,6 @@ class TypeRegistry { private: friend const runtime_internal::RuntimeTypeProvider& runtime_internal::GetRuntimeTypeProvider(const TypeRegistry& type_registry); - friend const runtime_internal::LegacyRuntimeTypeProvider& - runtime_internal::GetLegacyRuntimeTypeProvider( - const TypeRegistry& type_registry); friend std::shared_ptr> runtime_internal::GetEnumValueTable(const TypeRegistry& type_registry); @@ -115,7 +109,6 @@ class TypeRegistry { GetEnumValueTable() const; runtime_internal::RuntimeTypeProvider type_provider_; - runtime_internal::LegacyRuntimeTypeProvider legacy_type_provider_; absl::flat_hash_map enum_types_; // memoized fully qualified enumerator names. @@ -137,10 +130,6 @@ inline const RuntimeTypeProvider& GetRuntimeTypeProvider( const TypeRegistry& type_registry) { return type_registry.type_provider_; } -inline const LegacyRuntimeTypeProvider& GetLegacyRuntimeTypeProvider( - const TypeRegistry& type_registry) { - return type_registry.legacy_type_provider_; -} inline std::shared_ptr> GetEnumValueTable(const TypeRegistry& type_registry) { return type_registry.GetEnumValueTable();