From 8ee3f36e02482dff1a884a26d9f81d5f0fe32df5 Mon Sep 17 00:00:00 2001 From: CEL Dev Team Date: Fri, 13 Nov 2026 11:39:53 -0800 Subject: [PATCH] Separate modern CEL runtime configuration, expression compilation, and standard extensions from legacy V1 registries so modern consumers link zero deprecated evaluation code. Even after isolating legacy value types, the modern type registry, flat expression builder, shared runtime environment, and standard extension libraries remained coupled to legacy type and function registries. Because builders shared a single environment and extensions registered handlers through legacy adapter wrappers, modern servers linking the standard runtime paid the full code-size cost of the deprecated V1 evaluation stack. Moving legacy registry ownership and lifetime management into dedicated wrappers reserved for legacy expression builders lets the modern runtime environment operate standalone without touching legacy symbols. Splitting the extension registrars and protobuf visibility boundaries similarly allows modern callers to initialize standard extension functions directly against the modern function registry while keeping legacy wrapper headers intact for remaining V1 callers. PiperOrigin-RevId: 1021200960 --- common/BUILD | 105 +- common/legacy_value.cc | 1027 +++++++++++++---- common/legacy_value.h | 40 + common/value.cc | 279 +++++ common/values/custom_list_value.cc | 35 +- common/values/custom_map_value.cc | 43 +- common/values/legacy_list_value.cc | 29 +- common/values/legacy_list_value.h | 68 +- common/values/legacy_map_value.cc | 58 +- common/values/legacy_map_value.h | 84 +- common/values/legacy_struct_value.cc | 207 ++++ common/values/legacy_struct_value.h | 146 ++- common/values/list_value_builder.h | 42 - common/values/map_value_builder.h | 42 - common/values/parsed_message_value.h | 5 +- common/values/value_builder.cc | 484 ++------ env/BUILD | 18 +- env/env_std_extensions.cc | 8 +- env/runtime_std_extensions.cc | 10 +- eval/compiler/BUILD | 18 +- .../cel_expression_builder_flat_impl.cc | 22 +- .../cel_expression_builder_flat_impl.h | 18 +- .../cel_expression_builder_flat_impl_test.cc | 20 +- eval/compiler/flat_expr_builder.cc | 10 +- eval/compiler/flat_expr_builder.h | 7 +- .../flat_expr_builder_comprehensions_test.cc | 52 +- ...ilder_short_circuiting_conformance_test.cc | 6 +- eval/compiler/flat_expr_builder_test.cc | 396 +++++-- .../regex_precompilation_optimization_test.cc | 14 +- eval/eval/BUILD | 2 +- eval/eval/create_struct_step_test.cc | 4 +- eval/eval/evaluator_core_test.cc | 4 +- eval/eval/select_step.cc | 187 +-- eval/eval/select_step_test.cc | 384 ++++++ eval/public/BUILD | 2 +- eval/public/cel_expr_builder_factory.cc | 12 +- eval/public/structs/BUILD | 3 + .../cel_proto_wrap_value_to_message.cc | 36 +- .../cel_proto_wrap_value_to_message_test.cc | 32 + extensions/BUILD | 152 ++- extensions/comprehensions_v2_functions.cc | 117 -- extensions/comprehensions_v2_functions.h | 6 +- .../comprehensions_v2_functions_modern.cc | 140 +++ .../comprehensions_v2_functions_modern.h | 30 + extensions/encoders.cc | 88 -- extensions/encoders.h | 15 +- extensions/encoders_modern.cc | 109 ++ extensions/encoders_modern.h | 38 + extensions/math_ext.cc | 469 +------- extensions/math_ext.h | 10 +- extensions/math_ext_modern.cc | 483 ++++++++ extensions/math_ext_modern.h | 33 + extensions/regex_ext.cc | 333 +----- extensions/regex_ext.h | 98 +- extensions/regex_ext_modern.cc | 381 ++++++ extensions/regex_ext_modern.h | 129 +++ extensions/regex_ext_test.cc | 6 + extensions/regex_functions.cc | 215 +--- extensions/regex_functions.h | 14 +- extensions/regex_functions_modern.cc | 229 ++++ extensions/regex_functions_modern.h | 46 + extensions/select_optimization.cc | 6 +- extensions/select_optimization_test.cc | 3 - extensions/sets_functions.cc | 152 +-- extensions/sets_functions.h | 16 +- extensions/sets_functions_modern.cc | 160 +++ extensions/sets_functions_modern.h | 39 + extensions/strings.cc | 405 ------- extensions/strings.h | 43 +- extensions/strings_modern.cc | 415 +++++++ extensions/strings_modern.h | 66 ++ runtime/BUILD | 1 - runtime/internal/BUILD | 34 +- runtime/internal/legacy_runtime_env.cc | 47 + runtime/internal/legacy_runtime_env.h | 78 ++ .../internal/legacy_runtime_env_testing.cc | 42 + runtime/internal/legacy_runtime_env_testing.h | 29 + runtime/internal/runtime_env.h | 38 +- runtime/standard/BUILD | 71 +- runtime/standard/arithmetic_functions.cc | 355 +++--- runtime/standard/comparison_functions.cc | 348 +++--- runtime/standard/container_functions.cc | 108 +- .../container_membership_functions.cc | 444 ++++--- runtime/standard/equality_functions.cc | 393 ++----- runtime/standard/logical_functions.cc | 80 +- runtime/standard/regex_functions.cc | 56 +- runtime/standard/string_functions.cc | 184 +-- runtime/standard/time_functions.cc | 726 +++++------- runtime/standard/type_conversion_functions.cc | 754 ++++++------ runtime/type_registry.cc | 4 +- runtime/type_registry.h | 11 - 91 files changed, 7106 insertions(+), 5102 deletions(-) create mode 100644 extensions/comprehensions_v2_functions_modern.cc create mode 100644 extensions/comprehensions_v2_functions_modern.h create mode 100644 extensions/encoders_modern.cc create mode 100644 extensions/encoders_modern.h create mode 100644 extensions/math_ext_modern.cc create mode 100644 extensions/math_ext_modern.h create mode 100644 extensions/regex_ext_modern.cc create mode 100644 extensions/regex_ext_modern.h create mode 100644 extensions/regex_functions_modern.cc create mode 100644 extensions/regex_functions_modern.h create mode 100644 extensions/sets_functions_modern.cc create mode 100644 extensions/sets_functions_modern.h create mode 100644 extensions/strings_modern.cc create mode 100644 extensions/strings_modern.h create mode 100644 runtime/internal/legacy_runtime_env.cc create mode 100644 runtime/internal/legacy_runtime_env.h create mode 100644 runtime/internal/legacy_runtime_env_testing.cc create mode 100644 runtime/internal/legacy_runtime_env_testing.h 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();