From 158e823bda06ca8689e7942d93f4f0c0e5fa8d41 Mon Sep 17 00:00:00 2001 From: Jonathan Tatum Date: Thu, 15 Oct 2026 14:00:48 -0700 Subject: [PATCH] Optimize select paths for ParsedMessageValue using eagerly resolved field descriptors. Guarded by a flag that defaults to off. PiperOrigin-RevId: 1000374224 --- common/type.cc | 2 + common/type.h | 6 + common/type_spec_resolver.cc | 2 + common/types/struct_type.cc | 2 + common/types/struct_type.h | 5 + common/values/struct_value.cc | 2 + common/values/struct_value.h | 5 + eval/compiler/BUILD | 2 + eval/compiler/flat_expr_builder.cc | 8 +- eval/compiler/flat_expr_builder_extensions.cc | 20 + eval/compiler/flat_expr_builder_extensions.h | 3 + eval/eval/select_step.cc | 2 + extensions/BUILD | 8 + extensions/select_optimization.cc | 652 +++- extensions/select_optimization.h | 6 + extensions/select_optimization_test.cc | 3245 +++++++++++++---- 16 files changed, 3124 insertions(+), 846 deletions(-) diff --git a/common/type.cc b/common/type.cc index 2d3469540..5396bc57f 100644 --- a/common/type.cc +++ b/common/type.cc @@ -31,6 +31,8 @@ #include "common/types/types.h" #include "google/protobuf/descriptor.h" +#undef GetMessage + namespace cel { using ::google::protobuf::Descriptor; diff --git a/common/type.h b/common/type.h index e60452bfd..26bb73943 100644 --- a/common/type.h +++ b/common/type.h @@ -69,6 +69,10 @@ #include "google/protobuf/arena.h" #include "google/protobuf/descriptor.h" +// GetMessage in windows API headers might be defined as a macro. +#pragma push_macro("GetMessage") +#undef GetMessage + namespace cel { class Type; @@ -1300,4 +1304,6 @@ Type LegacyRuntimeType(absl::string_view name); } // namespace cel +#pragma pop_macro("GetMessage") + #endif // THIRD_PARTY_CEL_CPP_COMMON_TYPE_H_ diff --git a/common/type_spec_resolver.cc b/common/type_spec_resolver.cc index aab099255..c3b4bc171 100644 --- a/common/type_spec_resolver.cc +++ b/common/type_spec_resolver.cc @@ -33,6 +33,8 @@ #include "google/protobuf/arena.h" #include "google/protobuf/descriptor.h" +#undef GetMessage + namespace cel { namespace { diff --git a/common/types/struct_type.cc b/common/types/struct_type.cc index 69f531a2f..14e9fc4e2 100644 --- a/common/types/struct_type.cc +++ b/common/types/struct_type.cc @@ -22,6 +22,8 @@ #include "common/type.h" #include "common/types/types.h" +#undef GetMessage + namespace cel { absl::string_view StructType::name() const { diff --git a/common/types/struct_type.h b/common/types/struct_type.h index 6e20ea007..f947f88c3 100644 --- a/common/types/struct_type.h +++ b/common/types/struct_type.h @@ -33,6 +33,9 @@ #include "common/types/message_type.h" #include "common/types/types.h" +#pragma push_macro("GetMessage") +#undef GetMessage + namespace cel { class Type; @@ -155,4 +158,6 @@ inline std::ostream& operator<<(std::ostream& out, const StructType& type) { } // namespace cel +#pragma pop_macro("GetMessage") + #endif // THIRD_PARTY_CEL_CPP_COMMON_TYPES_STRUCT_TYPE_H_ diff --git a/common/values/struct_value.cc b/common/values/struct_value.cc index efbcaa514..8b7bb7cb1 100644 --- a/common/values/struct_value.cc +++ b/common/values/struct_value.cc @@ -39,6 +39,8 @@ #include "google/protobuf/io/zero_copy_stream.h" #include "google/protobuf/message.h" +#undef GetMessage + namespace cel { StructType StructValue::GetRuntimeType() const { diff --git a/common/values/struct_value.h b/common/values/struct_value.h index d096356c7..c46442c41 100644 --- a/common/values/struct_value.h +++ b/common/values/struct_value.h @@ -54,6 +54,9 @@ #include "google/protobuf/io/zero_copy_stream.h" #include "google/protobuf/message.h" +#pragma push_macro("GetMessage") +#undef GetMessage + namespace cel { class StructValue; @@ -370,4 +373,6 @@ using StructValueBuilderPtr = std::unique_ptr; } // namespace cel +#pragma pop_macro("GetMessage") + #endif // THIRD_PARTY_CEL_CPP_COMMON_VALUES_STRUCT_VALUE_H_ diff --git a/eval/compiler/BUILD b/eval/compiler/BUILD index a8f40749d..f9163e431 100644 --- a/eval/compiler/BUILD +++ b/eval/compiler/BUILD @@ -31,8 +31,10 @@ cc_library( ":resolver", "//base:ast", "//base:data", + "//common:ast", "//common:expr", "//common:type", + "//common:type_spec_resolver", "//common:value", "//eval/eval:evaluator_core", "//eval/eval:expression_step_logic", diff --git a/eval/compiler/flat_expr_builder.cc b/eval/compiler/flat_expr_builder.cc index 0d58c5864..e8a445186 100644 --- a/eval/compiler/flat_expr_builder.cc +++ b/eval/compiler/flat_expr_builder.cc @@ -1878,13 +1878,7 @@ void FlatExprVisitor::MaybeResolveType(const cel::Expr& expr) { if (it == type_map_.end()) { return; } - absl::StatusOr type = cel::ConvertTypeSpecToType( - it->second, extension_context_.type_reflector(), - extension_context_.MutableArena()); - if (!type.ok()) { - return; - } - extension_context_.SetResolvedType(expr.id(), *type); + extension_context_.TryResolveType(expr.id(), it->second); } size_t FlatExprVisitor::AddOrUpdateIdentifier(absl::string_view name) { diff --git a/eval/compiler/flat_expr_builder_extensions.cc b/eval/compiler/flat_expr_builder_extensions.cc index 3f308125a..ac62f5fd7 100644 --- a/eval/compiler/flat_expr_builder_extensions.cc +++ b/eval/compiler/flat_expr_builder_extensions.cc @@ -14,8 +14,10 @@ #include "eval/compiler/flat_expr_builder_extensions.h" #include +#include #include #include +#include #include #include @@ -27,7 +29,10 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/types/variant.h" +#include "common/ast.h" #include "common/expr.h" +#include "common/type.h" +#include "common/type_spec_resolver.h" #include "eval/eval/evaluator_core.h" namespace google::api::expr::runtime { @@ -350,6 +355,21 @@ absl::Status PlannerContext::ReplaceSubplan(const cel::Expr& node, return absl::OkStatus(); } +std::optional PlannerContext::TryResolveType( + int64_t expr_id, const cel::TypeSpec& ast_type) { + auto it = resolved_types_.find(expr_id); + if (it != resolved_types_.end()) { + return it->second; + } + absl::StatusOr type = + cel::ConvertTypeSpecToType(ast_type, type_reflector_, MutableArena()); + if (!type.ok()) { + return std::nullopt; + } + resolved_types_.insert(it, {expr_id, *type}); + return *type; +} + void ProgramBuilder::Reset() { root_ = nullptr; current_ = nullptr; diff --git a/eval/compiler/flat_expr_builder_extensions.h b/eval/compiler/flat_expr_builder_extensions.h index d6d3abb17..fb7a6d798 100644 --- a/eval/compiler/flat_expr_builder_extensions.h +++ b/eval/compiler/flat_expr_builder_extensions.h @@ -381,6 +381,9 @@ class PlannerContext { return std::nullopt; } + std::optional TryResolveType(int64_t expr_id, + const cel::TypeSpec& ast_type); + void SetResolvedType(int64_t expr_id, cel::Type type) { resolved_types_[expr_id] = std::move(type); } diff --git a/eval/eval/select_step.cc b/eval/eval/select_step.cc index 297eefa2b..217c659ee 100644 --- a/eval/eval/select_step.cc +++ b/eval/eval/select_step.cc @@ -26,6 +26,8 @@ #include "google/protobuf/descriptor.h" #include "google/protobuf/message.h" +#undef GetMessage + namespace google::api::expr::runtime { namespace { diff --git a/extensions/BUILD b/extensions/BUILD index 175662048..756fd8151 100644 --- a/extensions/BUILD +++ b/extensions/BUILD @@ -329,6 +329,7 @@ cc_library( "//common:expr", "//common:function_descriptor", "//common:kind", + "//common:legacy_value", "//common:native_type", "//common:type", "//common:value", @@ -337,6 +338,7 @@ cc_library( "//eval/eval:attribute_trail", "//eval/eval:evaluator_core", "//eval/eval:expression_step_base", + "//extensions/protobuf/internal:map_reflection", "//internal:casts", "//internal:number", "//internal:status_macros", @@ -346,6 +348,7 @@ cc_library( "//runtime/internal:runtime_friend_access", "//runtime/internal:runtime_impl", "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:overload", @@ -375,6 +378,7 @@ cc_test( "//common:expr", "//common:kind", "//common:native_type", + "//common:type", "//common:typeinfo", "//common:value", "//compiler", @@ -394,15 +398,19 @@ cc_test( "//internal:status_macros", "//internal:testing", "//parser", + "//runtime", "//runtime:activation", "//runtime:function_adapter", "//runtime:function_registry", + "//runtime:runtime_builder", "//runtime:runtime_issue", "//runtime:runtime_options", + "//runtime:standard_runtime_builder_factory", "//runtime:type_registry", "//runtime/internal:issue_collector", "//runtime/internal:runtime_env", "//runtime/internal:runtime_env_testing", + "//testutil:test_external_extensions_descriptor_set", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/log:absl_check", diff --git a/extensions/select_optimization.cc b/extensions/select_optimization.cc index 7747eeafe..5931ab954 100644 --- a/extensions/select_optimization.cc +++ b/extensions/select_optimization.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -26,12 +27,14 @@ #include "absl/algorithm/container.h" #include "absl/base/nullability.h" +#include "absl/base/optimization.h" #include "absl/container/flat_hash_map.h" #include "absl/functional/overload.h" #include "absl/log/absl_check.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/match.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "absl/types/variant.h" @@ -43,6 +46,7 @@ #include "common/expr.h" #include "common/function_descriptor.h" #include "common/kind.h" +#include "common/legacy_value.h" #include "common/native_type.h" #include "common/type.h" #include "common/value.h" @@ -51,6 +55,7 @@ #include "eval/eval/attribute_trail.h" #include "eval/eval/evaluator_core.h" #include "eval/eval/expression_step_base.h" +#include "extensions/protobuf/internal/map_reflection.h" #include "internal/casts.h" #include "internal/number.h" #include "internal/status_macros.h" @@ -61,7 +66,12 @@ #include "runtime/runtime_options.h" #include "google/protobuf/arena.h" #include "google/protobuf/descriptor.h" +#include "google/protobuf/map_field.h" #include "google/protobuf/message.h" +#include "google/protobuf/reflection.h" + +// undef for Windows +#undef GetMessage namespace cel::extensions { namespace { @@ -153,7 +163,7 @@ Expr MakeSelectPathExpr( // Returns a single select operation based on the inferred type of the operand // and the field name. If the operand type doesn't define the field, returns // nullopt. -std::optional GetSelectInstruction( +std::optional MakeFieldStepAst( const StructType& runtime_type, PlannerContext& planner_context, absl::string_view field_name) { auto field_or = planner_context.type_reflector() @@ -165,6 +175,30 @@ std::optional GetSelectInstruction( return std::nullopt; } +// Returns a qualifier instruction derived from a unoptimized ast. +absl::StatusOr MakeIndexStepAst( + const Constant& constant) { + if (constant.has_int_value()) { + return QualifierInstruction(constant.int_value()); + } else if (constant.has_uint_value()) { + return QualifierInstruction(constant.uint_value()); + } else if (constant.has_bool_value()) { + return QualifierInstruction(constant.bool_value()); + } else if (constant.has_string_value()) { + return QualifierInstruction(constant.string_value()); + } else if (constant.has_double_value()) { + cel::internal::Number number(constant.double_value()); + if (number.LosslessConvertibleToInt()) { + return QualifierInstruction(number.AsInt()); + } else if (number.LosslessConvertibleToUint()) { + return QualifierInstruction(number.AsUint()); + } + } + + return absl::InvalidArgumentError("invalid index constant for cel.attribute"); +} + +// Derives select step info from the optimized AST. absl::StatusOr SelectQualifierFromList(const ListExpr& list) { // Type codes may follow [number, name]; they're unused here. if (list.elements().size() < 2 || list.elements().size() > 4) { @@ -190,29 +224,7 @@ absl::StatusOr SelectQualifierFromList(const ListExpr& list) { field_name.const_expr().string_value()}; } -// Returns a qualifier instruction derived from a unoptimized ast. -absl::StatusOr SelectInstructionFromConstant( - const Constant& constant) { - if (constant.has_int_value()) { - return QualifierInstruction(constant.int_value()); - } else if (constant.has_uint_value()) { - return QualifierInstruction(constant.uint_value()); - } else if (constant.has_bool_value()) { - return QualifierInstruction(constant.bool_value()); - } else if (constant.has_string_value()) { - return QualifierInstruction(constant.string_value()); - } else if (constant.has_double_value()) { - cel::internal::Number number(constant.double_value()); - if (number.LosslessConvertibleToInt()) { - return QualifierInstruction(number.AsInt()); - } else if (number.LosslessConvertibleToUint()) { - return QualifierInstruction(number.AsUint()); - } - } - - return absl::InvalidArgumentError("invalid index constant for cel.attribute"); -} - +// Derives index step info from the optimized AST. absl::StatusOr SelectQualifierFromConstant( const Constant& constant) { if (constant.has_int_value()) { @@ -375,6 +387,9 @@ absl::StatusOr> SelectInstructionsFromCall( instructions.reserve(ast_path.size()); for (const ListExprElement& element : ast_path) { + if (element.optional()) { + return absl::InvalidArgumentError("Invalid cel.attribute call"); + } // Optimized field select. if (element.has_expr()) { const auto& element_expr = element.expr(); @@ -411,20 +426,20 @@ class RewriterImpl : public AstRewriterBase { const std::string& field_name = select.field(); // Select optimization can generalize to lists and maps, but for now only // support message traversal. - const TypeSpec checker_type = ast_.GetTypeOrDyn(operand.id()); - + const TypeSpec ast_type = ast_.GetTypeOrDyn(operand.id()); std::optional rt_type = - (checker_type.has_message_type()) - ? GetRuntimeType(checker_type.message_type().type()) - : std::nullopt; - if (rt_type.has_value() && (*rt_type).Is()) { + planner_context_.TryResolveType(operand.id(), ast_type); + if (!rt_type.has_value()) { + return; + } + if (rt_type->IsStruct()) { const StructType& runtime_type = rt_type->GetStruct(); std::optional field_or = - GetSelectInstruction(runtime_type, planner_context_, field_name); + MakeFieldStepAst(runtime_type, planner_context_, field_name); if (field_or.has_value()) { candidates_[&expr] = std::move(field_or).value(); } - } else if (checker_type.has_map_type()) { + } else if (rt_type->IsMap()) { candidates_[&expr] = QualifierInstruction(field_name); } // else @@ -439,8 +454,7 @@ class RewriterImpl : public AstRewriterBase { const auto& qualifier_expr = call.args()[1]; if (qualifier_expr.has_const_expr()) { - auto qualifier_or = - SelectInstructionFromConstant(qualifier_expr.const_expr()); + auto qualifier_or = MakeIndexStepAst(qualifier_expr.const_expr()); if (!qualifier_or.ok()) { // TODO(uncreated-issue/54): should warn, but by default warnings fail overall // program planning. @@ -548,11 +562,6 @@ class RewriterImpl : public AstRewriterBase { return candidates_.find(operand) != candidates_.end(); } - std::optional GetRuntimeType(absl::string_view type_name) { - return planner_context_.type_reflector().FindType(type_name).value_or( - std::nullopt); - } - void SetProgressStatus(const absl::Status& status) { if (progress_status_.ok() && !status.ok()) { progress_status_ = status; @@ -568,12 +577,101 @@ class RewriterImpl : public AstRewriterBase { absl::Status progress_status_; }; +struct FieldSelectStep { + const google::protobuf::Descriptor* absl_nonnull containing_type; + const google::protobuf::FieldDescriptor* absl_nonnull field; +}; + +struct MapSelectStep { + const google::protobuf::FieldDescriptor* absl_nonnull map_field; + const google::protobuf::FieldDescriptor* absl_nonnull key_field; + const google::protobuf::FieldDescriptor* absl_nonnull value_field; + // Store the error to propagate on access at runtime. + // The map key might alias string data owned by the SelectOptimizationImpl + // object. + absl::StatusOr key; +}; + +struct ListSelectStep { + const google::protobuf::FieldDescriptor* absl_nonnull repeated_field; +}; + +using TypedSelectStep = + std::variant; + +absl::StatusOr CreateProtoMapKey( + const google::protobuf::FieldDescriptor* absl_nonnull key_desc, + const cel::AttributeQualifier& key) { + google::protobuf::MapKey proto_key; + switch (key_desc->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + if (key.kind() != cel::Kind::kBool) { + return runtime_internal::CreateInvalidMapKeyTypeError( + key_desc->cpp_type_name()); + } + proto_key.SetBoolValue(*key.AsBool()); + return proto_key; + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: { + if (key.kind() != cel::Kind::kInt64) { + return runtime_internal::CreateInvalidMapKeyTypeError( + key_desc->cpp_type_name()); + } + int64_t key_value = *key.AsInt(); + if (key_value > std::numeric_limits::max() || + key_value < std::numeric_limits::lowest()) { + return absl::OutOfRangeError("integer overflow"); + } + proto_key.SetInt32Value(static_cast(key_value)); + return proto_key; + } + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: + if (key.kind() != cel::Kind::kInt64) { + return runtime_internal::CreateInvalidMapKeyTypeError( + key_desc->cpp_type_name()); + } + proto_key.SetInt64Value(*key.AsInt()); + return proto_key; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + if (key.kind() != cel::Kind::kString) { + return runtime_internal::CreateInvalidMapKeyTypeError( + key_desc->cpp_type_name()); + } + // This is an alias. The underlying string should be stable. + proto_key.SetStringValue(*key.AsString()); + return proto_key; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: { + if (key.kind() != cel::Kind::kUint64) { + return runtime_internal::CreateInvalidMapKeyTypeError( + key_desc->cpp_type_name()); + } + uint64_t key_value = *key.AsUint(); + if (key_value > std::numeric_limits::max()) { + return absl::OutOfRangeError("unsigned integer overflow"); + } + proto_key.SetUInt32Value(static_cast(key_value)); + return proto_key; + } + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + if (key.kind() != cel::Kind::kUint64) { + return runtime_internal::CreateInvalidMapKeyTypeError( + key_desc->cpp_type_name()); + } + proto_key.SetUInt64Value(*key.AsUint()); + return proto_key; + default: + return runtime_internal::CreateInvalidMapKeyTypeError( + key_desc->cpp_type_name()); + } +} + class OptimizedSelectImpl { public: OptimizedSelectImpl(std::vector select_path, + std::vector typed_select_path, std::vector qualifiers, bool presence_test, SelectOptimizationOptions options) : select_path_(std::move(select_path)), + typed_select_path_(std::move(typed_select_path)), qualifiers_(std::move(qualifiers)), presence_test_(presence_test), options_(options) @@ -588,8 +686,8 @@ class OptimizedSelectImpl { OptimizedSelectImpl(OptimizedSelectImpl&&) = default; OptimizedSelectImpl& operator=(OptimizedSelectImpl&&) = delete; - absl::StatusOr ApplySelect(ExecutionFrameBase& frame, - const StructValue& struct_value) const; + absl::Status ApplySelect(ExecutionFrameBase& frame, + Value& result_in_out) const; AttributeTrail GetAttributeTrail(const AttributeTrail& operand_trail) const; @@ -600,8 +698,12 @@ class OptimizedSelectImpl { } private: + absl::Status ContinueFromValue(ExecutionFrameBase& frame, Value step_result, + size_t next_index, Value& result_in_out) const; + std::optional attribute_; std::vector select_path_; + std::vector typed_select_path_; std::vector qualifiers_; bool presence_test_; SelectOptimizationOptions options_; @@ -638,34 +740,352 @@ absl::StatusOr> CheckForMarkedAttributes( return std::nullopt; } -absl::StatusOr OptimizedSelectImpl::ApplySelect( - ExecutionFrameBase& frame, const StructValue& struct_value) const { - auto value_or = - (options_.force_fallback_implementation) +bool SupportsCachedFieldDescriptor(const google::protobuf::Message* absl_nonnull message, + const TypedSelectStep& step) { + if (const FieldSelectStep* field_step = absl::get_if(&step); + field_step != nullptr) { + ABSL_DCHECK_EQ(field_step->field->containing_type(), + field_step->containing_type); + ABSL_DCHECK_EQ(field_step->containing_type->file()->pool(), + field_step->field->file()->pool()); + return message->GetDescriptor() == field_step->containing_type; + } + return false; +} + +Value WrapMessageField( + const google::protobuf::Message* absl_nonnull message, + const google::protobuf::FieldDescriptor* absl_nonnull field_descriptor, + bool is_unsafe, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + if (is_unsafe) { + return Value::WrapFieldUnsafe(ProtoWrapperTypeOptions::kUnsetNull, message, + field_descriptor, descriptor_pool, + message_factory, arena); + } + return Value::WrapField(ProtoWrapperTypeOptions::kUnsetNull, message, + field_descriptor, descriptor_pool, message_factory, + arena); +} + +Value WrapMapElement( + const google::protobuf::MapValueConstRef& value_ref, + const google::protobuf::Message* absl_nonnull message, + const google::protobuf::FieldDescriptor* absl_nonnull value_descriptor, + bool is_unsafe, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + if (is_unsafe) { + return Value::WrapMapFieldValueUnsafe(value_ref, message, value_descriptor, + descriptor_pool, message_factory, + arena); + } + return Value::WrapMapFieldValue(value_ref, message, value_descriptor, + descriptor_pool, message_factory, arena); +} + +Value WrapRepeatedElement( + int index, const google::protobuf::Message* absl_nonnull message, + const google::protobuf::FieldDescriptor* absl_nonnull repeated_descriptor, + bool is_unsafe, const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena) { + if (is_unsafe) { + return Value::WrapRepeatedFieldUnsafe(index, message, repeated_descriptor, + descriptor_pool, message_factory, + arena); + } + return Value::WrapRepeatedField(index, message, repeated_descriptor, + descriptor_pool, message_factory, arena); +} + +absl::StatusOr QualifyOrFallback( + ExecutionFrameBase& frame, const StructValue& struct_value, + absl::Span select_path, bool presence_test, + bool force_fallback_implementation) { + absl::StatusOr> value_and_count = + (force_fallback_implementation) ? absl::UnimplementedError("Forced fallback impl") - : struct_value.Qualify(select_path_, presence_test_, + : struct_value.Qualify(select_path, presence_test, frame.descriptor_pool(), frame.message_factory(), frame.arena()); - if (!value_or.ok()) { - if (value_or.status().code() == absl::StatusCode::kUnimplemented) { - return FallbackSelect(struct_value, select_path_, presence_test_, + if (!value_and_count.ok()) { + if (value_and_count.status().code() == absl::StatusCode::kUnimplemented) { + return FallbackSelect(struct_value, select_path, presence_test, frame.descriptor_pool(), frame.message_factory(), frame.arena()); } - return value_or.status(); + return value_and_count.status(); } - if (value_or->second < 0 || value_or->second >= select_path_.size()) { - return std::move(value_or->first); + if (value_and_count->second < 0 || + value_and_count->second >= select_path.size()) { + return std::move(value_and_count->first); } - return FallbackSelect( - value_or->first, - absl::MakeConstSpan(select_path_).subspan(value_or->second), - presence_test_, frame.descriptor_pool(), frame.message_factory(), - frame.arena()); + return FallbackSelect(value_and_count->first, + select_path.subspan(value_and_count->second), + presence_test, frame.descriptor_pool(), + frame.message_factory(), frame.arena()); +} + +absl::Status OptimizedSelectImpl::ContinueFromValue( + ExecutionFrameBase& frame, Value step_result, size_t next_index, + Value& result_in_out) const { + if (step_result.IsError() || next_index >= select_path_.size()) { + result_in_out = std::move(step_result); + return absl::OkStatus(); + } + absl::Span remaining = + absl::MakeConstSpan(select_path_).subspan(next_index); + if (step_result.IsStruct()) { + CEL_ASSIGN_OR_RETURN( + result_in_out, + QualifyOrFallback(frame, step_result.GetStruct(), remaining, + presence_test_, + options_.force_fallback_implementation)); + return absl::OkStatus(); + } + CEL_ASSIGN_OR_RETURN(result_in_out, + FallbackSelect(step_result, remaining, presence_test_, + frame.descriptor_pool(), + frame.message_factory(), frame.arena())); + return absl::OkStatus(); +} + +absl::Status OptimizedSelectImpl::ApplySelect(ExecutionFrameBase& frame, + Value& result_in_out) const { + if (options_.force_fallback_implementation || typed_select_path_.empty()) { + CEL_ASSIGN_OR_RETURN( + result_in_out, + QualifyOrFallback(frame, result_in_out.GetStruct(), select_path_, + presence_test_, + options_.force_fallback_implementation)); + return absl::OkStatus(); + } + const google::protobuf::Message* current_message = nullptr; + bool is_unsafe = false; + if (result_in_out.IsParsedMessage()) { + ParsedMessageValue parsed_message = result_in_out.GetParsedMessage(); + current_message = &(*parsed_message); + is_unsafe = + cel::interop_internal::IsUnsafeParsedMessageValue(parsed_message); + } else if (const google::protobuf::Message* legacy_message = + cel::interop_internal::GetLegacyMessage(result_in_out); + legacy_message != nullptr) { + current_message = legacy_message; + is_unsafe = true; + } else { + CEL_ASSIGN_OR_RETURN( + result_in_out, + QualifyOrFallback(frame, result_in_out.GetStruct(), select_path_, + presence_test_, + options_.force_fallback_implementation)); + return absl::OkStatus(); + } + + if (!SupportsCachedFieldDescriptor(current_message, typed_select_path_[0])) { + CEL_ASSIGN_OR_RETURN( + result_in_out, + QualifyOrFallback(frame, result_in_out.GetStruct(), select_path_, + presence_test_, + options_.force_fallback_implementation)); + return absl::OkStatus(); + } + + for (size_t i = 0; i < typed_select_path_.size(); ++i) { + const TypedSelectStep& step = typed_select_path_[i]; + + // handle singular + if (const FieldSelectStep* field_step = + absl::get_if(&step); + field_step != nullptr) { + const google::protobuf::FieldDescriptor* field_descriptor = field_step->field; + // We consumed the whole select path. Materialize the leaf. + if (i + 1 == select_path_.size()) { + if (presence_test_) { + result_in_out = + BoolValue{cel::UnsafeParsedMessageValue(current_message) + .HasField(field_descriptor)}; + return absl::OkStatus(); + } + result_in_out = WrapMessageField( + current_message, field_descriptor, is_unsafe, + frame.descriptor_pool(), frame.message_factory(), frame.arena()); + return absl::OkStatus(); + } + + if (i + 1 >= typed_select_path_.size()) { + Value step_result = WrapMessageField( + current_message, field_descriptor, is_unsafe, + frame.descriptor_pool(), frame.message_factory(), frame.arena()); + return ContinueFromValue(frame, std::move(step_result), i + 1, + result_in_out); + } + + if (field_descriptor->is_repeated()) { + // The next step in `typed_select_path_` is a `MapSelectStep` or + // `ListSelectStep` that operates on `current_message`. + continue; + } + + const google::protobuf::Message* next_message = + ¤t_message->GetReflection()->GetMessage(*current_message, + field_descriptor); + // Check for messages extended by overlays. This is redundant if all + // messages are from hermetic pools, which we cannot guarantee. + if (!SupportsCachedFieldDescriptor(next_message, + typed_select_path_[i + 1])) { + Value step_result = WrapMessageField( + current_message, field_descriptor, is_unsafe, + frame.descriptor_pool(), frame.message_factory(), frame.arena()); + return ContinueFromValue(frame, std::move(step_result), i + 1, + result_in_out); + } + current_message = next_message; + continue; + } + + // Handle map + if (const MapSelectStep* map_step = absl::get_if(&step); + map_step != nullptr) { + if (i + 1 == select_path_.size() && presence_test_) { + const AttributeQualifier& qualifier = + absl::get(select_path_[i]); + if (qualifier.kind() != cel::Kind::kString) { + result_in_out = ErrorValue::From( + runtime_internal::CreateNoMatchingOverloadError("has"), + frame.arena()); + return absl::OkStatus(); + } + if (!map_step->key.ok()) { + result_in_out = + ErrorValue::From(map_step->key.status(), frame.arena()); + return absl::OkStatus(); + } + bool found = protobuf_internal::ContainsMapKey( + *current_message->GetReflection(), *current_message, + *map_step->map_field, *map_step->key); + result_in_out = BoolValue{found}; + return absl::OkStatus(); + } + + if (!map_step->key.ok()) { + result_in_out = ErrorValue::From(map_step->key.status(), frame.arena()); + return absl::OkStatus(); + } + google::protobuf::MapValueConstRef value_ref; + bool found = protobuf_internal::LookupMapValue( + *current_message->GetReflection(), *current_message, + *map_step->map_field, *map_step->key, &value_ref); + if (!found) { + const AttributeQualifier& qualifier = + absl::get(select_path_[i]); + result_in_out = ErrorValue::From( + runtime_internal::CreateNoSuchKeyError(qualifier.ToString()), + frame.arena()); + return absl::OkStatus(); + } + + if (i + 1 == select_path_.size()) { + result_in_out = WrapMapElement( + value_ref, current_message, map_step->value_field, is_unsafe, + frame.descriptor_pool(), frame.message_factory(), frame.arena()); + return absl::OkStatus(); + } + + // We don't have a typed select step for the next step, so we fall back. + if (i + 1 >= typed_select_path_.size()) { + Value step_result = WrapMapElement( + value_ref, current_message, map_step->value_field, is_unsafe, + frame.descriptor_pool(), frame.message_factory(), frame.arena()); + return ContinueFromValue(frame, std::move(step_result), i + 1, + result_in_out); + } + + const google::protobuf::Message* next_message = &value_ref.GetMessageValue(); + if (!SupportsCachedFieldDescriptor(next_message, + typed_select_path_[i + 1])) { + Value step_result = WrapMapElement( + value_ref, current_message, map_step->value_field, is_unsafe, + frame.descriptor_pool(), frame.message_factory(), frame.arena()); + return ContinueFromValue(frame, std::move(step_result), i + 1, + result_in_out); + } + current_message = next_message; + continue; + } + + // handle list + if (const ListSelectStep* list_step = absl::get_if(&step); + list_step != nullptr) { + if (i + 1 == select_path_.size() && presence_test_) { + result_in_out = ErrorValue::From( + runtime_internal::CreateNoMatchingOverloadError("has"), + frame.arena()); + return absl::OkStatus(); + } + + const AttributeQualifier& qualifier = + absl::get(select_path_[i]); + if (qualifier.kind() != cel::Kind::kInt64) { + result_in_out = + ErrorValue::From(runtime_internal::CreateNoMatchingOverloadError( + cel::builtin::kIndex), + frame.arena()); + return absl::OkStatus(); + } + int64_t index = *qualifier.AsInt(); + const google::protobuf::Reflection* reflection = current_message->GetReflection(); + int size = + reflection->FieldSize(*current_message, list_step->repeated_field); + if (index < 0 || index >= size) { + result_in_out = ErrorValue::From( + absl::InvalidArgumentError(absl::StrCat( + "index out of bounds: index=", index, " size=", size)), + frame.arena()); + return absl::OkStatus(); + } + int int_index = static_cast(index); + + if (i + 1 == select_path_.size()) { + result_in_out = WrapRepeatedElement( + int_index, current_message, list_step->repeated_field, is_unsafe, + frame.descriptor_pool(), frame.message_factory(), frame.arena()); + return absl::OkStatus(); + } + + // We don't have a typed select step for the next step, so we fall back. + if (i + 1 >= typed_select_path_.size()) { + Value step_result = WrapRepeatedElement( + int_index, current_message, list_step->repeated_field, is_unsafe, + frame.descriptor_pool(), frame.message_factory(), frame.arena()); + return ContinueFromValue(frame, std::move(step_result), i + 1, + result_in_out); + } + + const google::protobuf::Message* next_message = &reflection->GetRepeatedMessage( + *current_message, list_step->repeated_field, int_index); + if (!SupportsCachedFieldDescriptor(next_message, + typed_select_path_[i + 1])) { + Value step_result = WrapRepeatedElement( + int_index, current_message, list_step->repeated_field, is_unsafe, + frame.descriptor_pool(), frame.message_factory(), frame.arena()); + return ContinueFromValue(frame, std::move(step_result), i + 1, + result_in_out); + } + current_message = next_message; + continue; + } + + ABSL_UNREACHABLE(); + return absl::InternalError("Unknown select step type"); + } + + return absl::OkStatus(); } AttributeTrail OptimizedSelectImpl::GetAttributeTrail( @@ -691,31 +1111,16 @@ class StackMachineImpl : public ExpressionStepBase { void Evaluate(ExecutionFrame* frame) const override; private: - // Get the effective attribute for the optimized select expression. - // Assumes the operand is the top of stack if the attribute wasn't known at - // plan time. - AttributeTrail GetAttributeTrail(ExecutionFrame* frame) const; - OptimizedSelectImpl impl_; }; -AttributeTrail StackMachineImpl::GetAttributeTrail( - ExecutionFrame* frame) const { - const auto& attr = frame->value_stack().PeekAttribute(); - return impl_.GetAttributeTrail(attr); -} - void StackMachineImpl::Evaluate(ExecutionFrame* frame) const { - // Default empty. - AttributeTrail attribute_trail; // TODO(uncreated-issue/51): add support for variable qualifiers and string literal // variable names. - constexpr size_t kStackInputs = 1; - // For now, we expect the operand to be top of stack. - const Value& operand = frame->value_stack().Peek(); + Value& top = frame->value_stack().Peek(); - if (operand->Is() || operand->Is()) { + if (top.Is() || top.Is()) { // Just forward the error which is already top of stack. return; } @@ -725,7 +1130,8 @@ void StackMachineImpl::Evaluate(ExecutionFrame* frame) const { // When possible, this is computed at plan time based on the optimized // select arguments. // TODO(uncreated-issue/51): add support variable qualifiers - attribute_trail = GetAttributeTrail(frame); + AttributeTrail& attribute_trail = frame->value_stack().PeekAttribute(); + attribute_trail = impl_.GetAttributeTrail(attribute_trail); absl::StatusOr> value = CheckForMarkedAttributes(*frame, attribute_trail); if (!value.ok()) { @@ -733,27 +1139,21 @@ void StackMachineImpl::Evaluate(ExecutionFrame* frame) const { return; } if (value->has_value()) { - frame->value_stack().Pop(kStackInputs); - frame->value_stack().Push(std::move(*value).value(), - std::move(attribute_trail)); + top = std::move(*value).value(); return; } } - if (!operand->Is()) { + if (!top.Is()) { frame->Abort(absl::InvalidArgumentError( "Expected struct type for select optimization.")); return; } - absl::StatusOr result = impl_.ApplySelect(*frame, operand.GetStruct()); - if (!result.ok()) { - frame->Abort(std::move(result).status()); - return; + absl::Status status = impl_.ApplySelect(*frame, top); + if (!status.ok()) { + frame->Abort(std::move(status)); } - - frame->value_stack().Pop(kStackInputs); - frame->value_stack().Push(*std::move(result), std::move(attribute_trail)); } class SelectOptimizer : public ProgramOptimizer { @@ -771,6 +1171,74 @@ class SelectOptimizer : public ProgramOptimizer { SelectOptimizationOptions options_; }; +std::vector BuildTypedSelectPath( + PlannerContext& context, const Expr& operand, + absl::Span instructions) { + std::vector typed_select_path; + std::optional current_type = context.GetResolvedType(operand.id()); + for (const SelectQualifier& instruction : instructions) { + if (!current_type.has_value()) { + break; + } + if (const FieldSpecifier* field_specifier = + absl::get_if(&instruction); + field_specifier != nullptr) { + if (!current_type->IsMessage()) { + break; + } + cel::MessageType message_type = current_type->GetMessage(); + absl::StatusOr> field = + context.type_reflector().FindStructTypeFieldByName( + message_type, field_specifier->name); + if (!field.ok() || !field->has_value() || !(*field)->IsMessage()) { + break; + } + const google::protobuf::Descriptor* containing_descriptor = + message_type.descriptor(); + const google::protobuf::FieldDescriptor* field_descriptor = + (*field)->GetMessage().descriptor(); + if (containing_descriptor->file()->pool() != + field_descriptor->file()->pool()) { + break; + } + typed_select_path.push_back( + FieldSelectStep{containing_descriptor, field_descriptor}); + current_type = (*field)->GetType(); + } else if (const AttributeQualifier* attr_qualifier = + absl::get_if(&instruction); + attr_qualifier != nullptr) { + if (typed_select_path.empty() || + !absl::holds_alternative(typed_select_path.back())) { + break; + } + const google::protobuf::FieldDescriptor* prev_field = + absl::get(typed_select_path.back()).field; + if (prev_field->is_map() && current_type->IsMap()) { + const google::protobuf::Descriptor* entry_descriptor = prev_field->message_type(); + const google::protobuf::FieldDescriptor* key_field = entry_descriptor->map_key(); + const google::protobuf::FieldDescriptor* value_field = + entry_descriptor->map_value(); + if (key_field == nullptr || value_field == nullptr) { + break; + } + typed_select_path.push_back( + MapSelectStep{prev_field, key_field, value_field, + CreateProtoMapKey(key_field, *attr_qualifier)}); + current_type = current_type->GetMap().GetValue(); + } else if (prev_field->is_repeated() && !prev_field->is_map() && + current_type->IsList()) { + typed_select_path.push_back(ListSelectStep{prev_field}); + current_type = current_type->GetList().GetElement(); + } else { + break; + } + } else { + break; + } + } + return typed_select_path; +} + absl::Status SelectOptimizer::OnPostVisit(PlannerContext& context, const Expr& node) { if (!node.has_call_expr()) { @@ -827,7 +1295,14 @@ absl::Status SelectOptimizer::OnPostVisit(PlannerContext& context, return absl::OkStatus(); } - OptimizedSelectImpl impl(std::move(instructions), std::move(qualifiers), + std::vector typed_select_path; + if (options_.enable_field_descriptor_caching && + context.options().enable_typed_field_access) { + typed_select_path = BuildTypedSelectPath(context, operand, instructions); + } + + OptimizedSelectImpl impl(std::move(instructions), + std::move(typed_select_path), std::move(qualifiers), presence_test, options_); google::api::expr::runtime::ExecutionPath path; @@ -873,6 +1348,9 @@ google::api::expr::runtime::ProgramOptimizerFactory CreateSelectOptimizationProgramOptimizer( const SelectOptimizationOptions& options) { return [=](PlannerContext& context, const Ast& ast) { + if (options.enable_field_descriptor_caching) { + context.set_resolve_types(); + } return std::make_unique(options); }; } diff --git a/extensions/select_optimization.h b/extensions/select_optimization.h index 8beeb1bed..e362094a3 100644 --- a/extensions/select_optimization.h +++ b/extensions/select_optimization.h @@ -44,6 +44,12 @@ struct SelectOptimizationOptions { // optimization is already applied by a compiler / static optimizer, this // option has no effect. int min_field_depth_for_optimization = 0; + + // Whether to try to cache field descriptor lookups for the select + // optimization at plan time. + // + // This is temporary to make the change rollback safe for existing users. + bool enable_field_descriptor_caching = false; }; // Enable select optimization on the given RuntimeBuilder, replacing long diff --git a/extensions/select_optimization_test.cc b/extensions/select_optimization_test.cc index 7e15dc897..d12c3b340 100644 --- a/extensions/select_optimization_test.cc +++ b/extensions/select_optimization_test.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,7 @@ #include "common/expr.h" #include "common/kind.h" #include "common/native_type.h" +#include "common/type.h" #include "common/typeinfo.h" #include "common/value.h" #include "common/values/custom_struct_value.h" @@ -71,12 +73,17 @@ #include "runtime/internal/issue_collector.h" #include "runtime/internal/runtime_env.h" #include "runtime/internal/runtime_env_testing.h" +#include "runtime/runtime.h" +#include "runtime/runtime_builder.h" #include "runtime/runtime_issue.h" #include "runtime/runtime_options.h" +#include "runtime/standard_runtime_builder_factory.h" #include "runtime/type_registry.h" +#include "testutil/test_external_extensions_descriptor_set.h" #include "cel/expr/conformance/proto2/test_all_types.pb.h" #include "google/protobuf/arena.h" #include "google/protobuf/descriptor.h" +#include "google/protobuf/dynamic_message.h" #include "google/protobuf/extension_set.h" #include "google/protobuf/message.h" #include "google/protobuf/text_format.h" @@ -756,6 +763,58 @@ TEST_F(SelectOptimizationTest, AstTransformAnyDotTraversal) { EXPECT_EQ(operand.ident_expr().name(), "nested_test_all_types"); } +TEST_F(SelectOptimizationTest, AstTransformStructDotTraversal) { + ASSERT_OK_AND_ASSIGN( + std::unique_ptr ast, + CompileForTestCase( + "nested_test_all_types.payload.single_struct.key.subkey")); + + SelectOptimizationAstUpdater updater; + EXPECT_THAT(updater.UpdateAst(context_, *ast), IsOk()); + + // `.key` on `single_struct` (MapType) is included in the attribute call, + // while `.subkey` on `dyn` (`google.protobuf.Value`) remains an outer select. + EXPECT_EQ(ast->root_expr().select_expr().field(), "subkey"); + const CallExpr& attr_call = + ast->root_expr().select_expr().operand().call_expr(); + EXPECT_EQ(attr_call.function(), "cel.@attribute"); + + ASSERT_THAT(attr_call.args(), SizeIs(2)); + + EXPECT_THAT( + attr_call.args()[1].list_expr().elements(), + ElementsAre(SelectFieldEntry(2, "payload"), + SelectFieldEntry(103, "single_struct"), + SelectQualifier(AttributeQualifier::OfString("key")))); + + const Expr& operand = attr_call.args()[0]; + EXPECT_EQ(operand.ident_expr().name(), "nested_test_all_types"); +} + +TEST_F(SelectOptimizationTest, AstTransformValueDotTraversal) { + ASSERT_OK_AND_ASSIGN( + std::unique_ptr ast, + CompileForTestCase("nested_test_all_types.payload.single_value.key")); + + SelectOptimizationAstUpdater updater; + EXPECT_THAT(updater.UpdateAst(context_, *ast), IsOk()); + + // `.key` on `single_value` (`dyn`) is not merged into the attribute call. + EXPECT_EQ(ast->root_expr().select_expr().field(), "key"); + const CallExpr& attr_call = + ast->root_expr().select_expr().operand().call_expr(); + EXPECT_EQ(attr_call.function(), "cel.@attribute"); + + ASSERT_THAT(attr_call.args(), SizeIs(2)); + + EXPECT_THAT(attr_call.args()[1].list_expr().elements(), + ElementsAre(SelectFieldEntry(2, "payload"), + SelectFieldEntry(104, "single_value"))); + + const Expr& operand = attr_call.args()[0]; + EXPECT_EQ(operand.ident_expr().name(), "nested_test_all_types"); +} + TEST_F(SelectOptimizationTest, AstTransformRepeated) { // nested_test_all_types.payload.repeated_nested_message[1].bb ASSERT_OK_AND_ASSIGN( @@ -825,8 +884,11 @@ TEST_F(SelectOptimizationTest, ProgramOptimizerUnoptimizedAst) { google::protobuf::LinkMessageReflection(); FlatExprBuilder builder(env_, runtime_options_); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = true; - builder.AddProgramOptimizer(CreateSelectOptimizationProgramOptimizer()); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); // nested_test_all_types.child.payload.standalone_message.bb ASSERT_OK_AND_ASSIGN( @@ -859,6 +921,46 @@ TEST_F(SelectOptimizationTest, ProgramOptimizerUnoptimizedAst) { EXPECT_EQ(result.GetInt().NativeValue(), 42); } +TEST_F(SelectOptimizationTest, FieldDescriptorCachingDisabled) { + google::protobuf::LinkMessageReflection(); + + FlatExprBuilder builder(env_, runtime_options_); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = false; + + builder.AddAstTransform( + std::make_unique(select_options)); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); + + ASSERT_OK_AND_ASSIGN( + std::unique_ptr ast, + CompileForTestCase( + "nested_test_all_types.child.payload.standalone_message.bb")); + + ASSERT_OK_AND_ASSIGN(FlatExpression plan, + builder.CreateExpressionImpl(std::move(ast), nullptr)); + + NestedTestAllTypes* var = google::protobuf::Arena::Create(&arena_); + var->mutable_child()->mutable_payload()->mutable_standalone_message()->set_bb( + 42); + + cel::Activation act; + act.InsertOrAssignValue("nested_test_all_types", + cel::ParsedMessageValue(var, &arena_)); + + auto state = plan.MakeEvaluatorState(env_->descriptor_pool.get(), + env_->MutableMessageFactory(), &arena_); + ASSERT_OK_AND_ASSIGN( + Value result, + plan.EvaluateWithCallback( + act, /*embedder_context=*/nullptr, + google::api::expr::runtime::EvaluationListener(), state)); + + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); +} + TEST_F(SelectOptimizationTest, MissingAttributeIndependentOfUnknown) { google::protobuf::LinkMessageReflection(); @@ -867,9 +969,13 @@ TEST_F(SelectOptimizationTest, MissingAttributeIndependentOfUnknown) { options.enable_missing_attribute_errors = true; FlatExprBuilder builder(env_, options); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = true; - builder.AddAstTransform(std::make_unique()); - builder.AddProgramOptimizer(CreateSelectOptimizationProgramOptimizer()); + builder.AddAstTransform( + std::make_unique(select_options)); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); ASSERT_OK_AND_ASSIGN( std::unique_ptr ast, @@ -920,9 +1026,13 @@ TEST_F(SelectOptimizationTest, NullUnboxingOptionHonored) { options.enable_empty_wrapper_null_unboxing = true; FlatExprBuilder builder(env_, options); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = true; - builder.AddAstTransform(std::make_unique()); - builder.AddProgramOptimizer(CreateSelectOptimizationProgramOptimizer()); + builder.AddAstTransform( + std::make_unique(select_options)); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); // nested_test_all_types.payload.single_int64_wrapper ASSERT_OK_AND_ASSIGN( @@ -933,16 +1043,64 @@ TEST_F(SelectOptimizationTest, NullUnboxingOptionHonored) { builder.CreateExpressionImpl(std::move(ast), nullptr)); cel::Activation act; - // activation only uses a ptr to the underlying message, persist them. - NestedTestAllTypes var; + NestedTestAllTypes generated_var; ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( R"pb( payload {} )pb", - &var)); - ASSERT_THAT(TestBindLegacyMessage("nested_test_all_types", var, &arena_, act), - IsOk()); + &generated_var)); + google::protobuf::Message* var = + runtime_internal::MakeTestingEnvDynamicProto(generated_var, &arena_); + ASSERT_THAT( + TestBindLegacyMessage("nested_test_all_types", *var, &arena_, act), + IsOk()); + + auto state = plan.MakeEvaluatorState(env_->descriptor_pool.get(), + env_->MutableMessageFactory(), &arena_); + ASSERT_OK_AND_ASSIGN( + Value result, + plan.EvaluateWithCallback( + act, /*embedder_context=*/nullptr, + google::api::expr::runtime::EvaluationListener(), state)); + + ASSERT_TRUE(result->Is()) << result->DebugString(); +} + +TEST_F(SelectOptimizationTest, NullUnboxingOptionHonoredModernParsedMessage) { + google::protobuf::LinkMessageReflection(); + + RuntimeOptions options = runtime_options_; + options.enable_empty_wrapper_null_unboxing = true; + + FlatExprBuilder builder(env_, options); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = true; + + builder.AddAstTransform( + std::make_unique(select_options)); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); + + // nested_test_all_types.payload.single_int64_wrapper + ASSERT_OK_AND_ASSIGN( + std::unique_ptr ast, + CompileForTestCase("nested_test_all_types.payload.single_int64_wrapper")); + + ASSERT_OK_AND_ASSIGN(FlatExpression plan, + builder.CreateExpressionImpl(std::move(ast), nullptr)); + + cel::Activation act; + NestedTestAllTypes generated_var; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb( + payload {} + )pb", + &generated_var)); + google::protobuf::Message* var = + runtime_internal::MakeTestingEnvDynamicProto(generated_var, &arena_); + act.InsertOrAssignValue("nested_test_all_types", + cel::ParsedMessageValue(var, &arena_)); auto state = plan.MakeEvaluatorState(env_->descriptor_pool.get(), env_->MutableMessageFactory(), &arena_); @@ -969,10 +1127,19 @@ struct ProgramOptimizerTestCase { class SelectOptimizationProgramOptimizerTest : public SelectOptimizationTest, - public testing::WithParamInterface {}; + public testing::WithParamInterface< + std::tuple> { + protected: + const ProgramOptimizerTestCase& test_case() const { + return std::get<0>(GetParam()); + } + + bool enable_field_descriptor_caching() const { + return std::get<1>(GetParam()); + } +}; TEST_P(SelectOptimizationProgramOptimizerTest, Default) { - const ProgramOptimizerTestCase& test_case = GetParam(); google::protobuf::LinkMessageReflection(); RuntimeOptions options = runtime_options_; @@ -980,42 +1147,185 @@ TEST_P(SelectOptimizationProgramOptimizerTest, Default) { options.enable_missing_attribute_errors = true; FlatExprBuilder builder(env_, options); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = + enable_field_descriptor_caching(); - builder.AddAstTransform(std::make_unique()); - builder.AddProgramOptimizer(CreateSelectOptimizationProgramOptimizer()); + builder.AddAstTransform( + std::make_unique(select_options)); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, - CompileForTestCase(test_case.expr)); + CompileForTestCase(test_case().expr)); ASSERT_OK_AND_ASSIGN(FlatExpression plan, builder.CreateExpressionImpl(std::move(ast), nullptr)); cel::Activation act; - // activation only uses a ptr to the underlying message, persist them. - std::vector> vars; - for (const auto& entry : test_case.vars) { - vars.push_back(std::make_unique()); + for (const auto& entry : test_case().vars) { + NestedTestAllTypes generated_var; ASSERT_TRUE( - google::protobuf::TextFormat::ParseFromString(entry.second, vars.back().get())); - ASSERT_THAT(TestBindLegacyMessage(entry.first, *vars.back(), &arena_, act), - IsOk()); + google::protobuf::TextFormat::ParseFromString(entry.second, &generated_var)); + google::protobuf::Message* var = + runtime_internal::MakeTestingEnvDynamicProto(generated_var, &arena_); + ASSERT_THAT(TestBindLegacyMessage(entry.first, *var, &arena_, act), IsOk()); } - if (test_case.setup_activation != nullptr) { - ASSERT_THAT(test_case.setup_activation(&arena_, act), IsOk()); + if (test_case().setup_activation != nullptr) { + ASSERT_THAT(test_case().setup_activation(&arena_, act), IsOk()); } - auto state = plan.MakeEvaluatorState(env_->descriptor_pool.get(), - env_->MutableMessageFactory(), &arena_); + google::api::expr::runtime::FlatExpressionEvaluatorState state = + plan.MakeEvaluatorState(env_->descriptor_pool.get(), + env_->MutableMessageFactory(), &arena_); + absl::StatusOr result = plan.EvaluateWithCallback( + act, /*embedder_context=*/nullptr, + google::api::expr::runtime::EvaluationListener(), state); + + ASSERT_NO_FATAL_FAILURE(test_case().expectations(result)); +} + +TEST_P(SelectOptimizationProgramOptimizerTest, ModernParsedMessage) { + google::protobuf::LinkMessageReflection(); + + RuntimeOptions options = runtime_options_; + options.unknown_processing = UnknownProcessingOptions::kAttributeAndFunction; + options.enable_missing_attribute_errors = true; + + FlatExprBuilder builder(env_, options); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = + enable_field_descriptor_caching(); + + builder.AddAstTransform( + std::make_unique(select_options)); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); + + ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, + CompileForTestCase(test_case().expr)); + + ASSERT_OK_AND_ASSIGN(FlatExpression plan, + builder.CreateExpressionImpl(std::move(ast), nullptr)); + + cel::Activation act; + for (const auto& entry : test_case().vars) { + NestedTestAllTypes generated_var; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(entry.second, &generated_var)); + google::protobuf::Message* var = + runtime_internal::MakeTestingEnvDynamicProto(generated_var, &arena_); + act.InsertOrAssignValue(entry.first, cel::ParsedMessageValue(var, &arena_)); + } + + if (test_case().setup_activation != nullptr) { + ASSERT_THAT(test_case().setup_activation(&arena_, act), IsOk()); + } + + google::api::expr::runtime::FlatExpressionEvaluatorState state = + plan.MakeEvaluatorState(env_->descriptor_pool.get(), + env_->MutableMessageFactory(), &arena_); + absl::StatusOr result = plan.EvaluateWithCallback( + act, /*embedder_context=*/nullptr, + google::api::expr::runtime::EvaluationListener(), state); + + ASSERT_NO_FATAL_FAILURE(test_case().expectations(result)); +} + +TEST_P(SelectOptimizationProgramOptimizerTest, UnsafeParsedMessage) { + google::protobuf::LinkMessageReflection(); + + RuntimeOptions options = runtime_options_; + options.unknown_processing = UnknownProcessingOptions::kAttributeAndFunction; + options.enable_missing_attribute_errors = true; + + FlatExprBuilder builder(env_, options); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = + enable_field_descriptor_caching(); + + builder.AddAstTransform( + std::make_unique(select_options)); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); + + ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, + CompileForTestCase(test_case().expr)); + + ASSERT_OK_AND_ASSIGN(FlatExpression plan, + builder.CreateExpressionImpl(std::move(ast), nullptr)); + + cel::Activation act; + for (const auto& entry : test_case().vars) { + NestedTestAllTypes generated_var; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(entry.second, &generated_var)); + google::protobuf::Message* var = + runtime_internal::MakeTestingEnvDynamicProto(generated_var, &arena_); + act.InsertOrAssignValue(entry.first, cel::UnsafeParsedMessageValue(var)); + } + + if (test_case().setup_activation != nullptr) { + ASSERT_THAT(test_case().setup_activation(&arena_, act), IsOk()); + } + + google::api::expr::runtime::FlatExpressionEvaluatorState state = + plan.MakeEvaluatorState(env_->descriptor_pool.get(), + env_->MutableMessageFactory(), &arena_); absl::StatusOr result = plan.EvaluateWithCallback( act, /*embedder_context=*/nullptr, google::api::expr::runtime::EvaluationListener(), state); - ASSERT_NO_FATAL_FAILURE(test_case.expectations(result)); + ASSERT_NO_FATAL_FAILURE(test_case().expectations(result)); +} + +TEST_P(SelectOptimizationProgramOptimizerTest, DifferentDescriptorPool) { + google::protobuf::LinkMessageReflection(); + + RuntimeOptions options = runtime_options_; + options.unknown_processing = UnknownProcessingOptions::kAttributeAndFunction; + options.enable_missing_attribute_errors = true; + + FlatExprBuilder builder(env_, options); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = + enable_field_descriptor_caching(); + + builder.AddAstTransform( + std::make_unique(select_options)); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); + + ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, + CompileForTestCase(test_case().expr)); + + ASSERT_OK_AND_ASSIGN(FlatExpression plan, + builder.CreateExpressionImpl(std::move(ast), nullptr)); + + cel::Activation act; + for (const auto& entry : test_case().vars) { + NestedTestAllTypes* var = + google::protobuf::Arena::Create(&arena_); + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(entry.second, var)); + act.InsertOrAssignValue(entry.first, cel::ParsedMessageValue(var, &arena_)); + } + + if (test_case().setup_activation != nullptr) { + ASSERT_THAT(test_case().setup_activation(&arena_, act), IsOk()); + } + + google::api::expr::runtime::FlatExpressionEvaluatorState state = + plan.MakeEvaluatorState(env_->descriptor_pool.get(), + env_->MutableMessageFactory(), &arena_); + absl::StatusOr result = plan.EvaluateWithCallback( + act, /*embedder_context=*/nullptr, + google::api::expr::runtime::EvaluationListener(), state); + + ASSERT_NO_FATAL_FAILURE(test_case().expectations(result)); } TEST_P(SelectOptimizationProgramOptimizerTest, ForceFallbackImpl) { - const ProgramOptimizerTestCase& test_case = GetParam(); google::protobuf::LinkMessageReflection(); RuntimeOptions options = runtime_options_; @@ -1025,13 +1335,16 @@ TEST_P(SelectOptimizationProgramOptimizerTest, ForceFallbackImpl) { FlatExprBuilder builder(env_, options); SelectOptimizationOptions select_options; select_options.force_fallback_implementation = true; + select_options.enable_field_descriptor_caching = + enable_field_descriptor_caching(); - builder.AddAstTransform(std::make_unique()); + builder.AddAstTransform( + std::make_unique(select_options)); builder.AddProgramOptimizer( CreateSelectOptimizationProgramOptimizer(select_options)); ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, - CompileForTestCase(test_case.expr)); + CompileForTestCase(test_case().expr)); ASSERT_OK_AND_ASSIGN(FlatExpression plan, builder.CreateExpressionImpl(std::move(ast), nullptr)); @@ -1039,7 +1352,7 @@ TEST_P(SelectOptimizationProgramOptimizerTest, ForceFallbackImpl) { cel::Activation act; // activation only uses a ptr to the underlying message, persist them. std::vector> vars; - for (const auto& entry : test_case.vars) { + for (const auto& entry : test_case().vars) { vars.push_back(std::make_unique()); ASSERT_TRUE( google::protobuf::TextFormat::ParseFromString(entry.second, vars.back().get())); @@ -1047,746 +1360,1857 @@ TEST_P(SelectOptimizationProgramOptimizerTest, ForceFallbackImpl) { IsOk()); } - if (test_case.setup_activation != nullptr) { - ASSERT_THAT(test_case.setup_activation(&arena_, act), IsOk()); + if (test_case().setup_activation != nullptr) { + ASSERT_THAT(test_case().setup_activation(&arena_, act), IsOk()); } - auto state = plan.MakeEvaluatorState(env_->descriptor_pool.get(), - env_->MutableMessageFactory(), &arena_); + google::api::expr::runtime::FlatExpressionEvaluatorState state = + plan.MakeEvaluatorState(env_->descriptor_pool.get(), + env_->MutableMessageFactory(), &arena_); absl::StatusOr result = plan.EvaluateWithCallback( act, /*embedder_context=*/nullptr, google::api::expr::runtime::EvaluationListener(), state); - ASSERT_NO_FATAL_FAILURE(test_case.expectations(result)); + ASSERT_NO_FATAL_FAILURE(test_case().expectations(result)); } INSTANTIATE_TEST_SUITE_P( TestCases, SelectOptimizationProgramOptimizerTest, - testing::ValuesIn({ - { - "chained_select_success", - "nested_test_all_types.child.payload.standalone_message.bb", - {{"nested_test_all_types", - R"pb( - child { payload { standalone_message { bb: 42 } } } - )pb"}}, - ActivationSetupFn(), - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 42); - }, - }, - { - "chained_select_defaults_success", - "nested_test_all_types.child.payload.standalone_message.bb", - {{"nested_test_all_types", R"pb()pb"}}, - ActivationSetupFn(), - - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 0); - }, - }, - { - "chained_select_partial_success", - "nested_test_all_types.child.payload.standalone_message.bb", - {}, - [](google::protobuf::Arena* arena, Activation& act) { - auto* custom_struct = - google::protobuf::Arena::Create(arena, - IntValue(42)); - act.InsertOrAssignValue("nested_test_all_types", - CustomStructValue(custom_struct, arena)); - return absl::OkStatus(); - }, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 42); - }, - }, - { - "chained_select_presence_partial_present", - "has(nested_test_all_types.child.payload.standalone_message.bb)", - {}, - [](google::protobuf::Arena* arena, Activation& act) { - auto* custom_struct = - google::protobuf::Arena::Create(arena, - IntValue(42)); - act.InsertOrAssignValue("nested_test_all_types", - CustomStructValue(custom_struct, arena)); - return absl::OkStatus(); - }, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_TRUE(result.GetBool().NativeValue()); - }, - }, - { - "chained_select_not_bound", - "nested_test_all_types.child.payload.standalone_message.bb", - {}, // not set - ActivationSetupFn(), - - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_THAT(result.GetError().NativeValue(), - StatusIs(absl::StatusCode::kUnknown, - HasSubstr("nested_test_all_types"))); - }, - }, - { - // Some clients will use maps to represent a protobuf message at - // runtime. This is not yet supported. - "chained_select_map_as_root_unsupported", - "nested_test_all_types.child.payload.standalone_message.bb", - {}, // not set - [](google::protobuf::Arena* arena, Activation& act) -> absl::Status { - auto builder = cel::NewMapValueBuilder(arena); - CEL_RETURN_IF_ERROR(builder->Put( - cel::StringValue::WrapUnsafe("child"), cel::NullValue())); - - auto value = std::move(*builder).Build(); - - act.InsertOrAssignValue("nested_test_all_types", - std::move(value)); - - return absl::OkStatus(); - }, - - [](const absl::StatusOr& got) { - EXPECT_THAT(got.status(), - StatusIs(absl::StatusCode::kInvalidArgument)); - }, - }, - { - // Some clients will use maps to represent a protobuf at runtime, - // this is not yet supported. - "chained_select_noncontainer_as_root_unsupported", - "nested_test_all_types.child.payload.standalone_message.bb", - {}, // not set - [](google::protobuf::Arena* arena, Activation& act) { - act.InsertOrAssignValue("nested_test_all_types", - cel::DurationValue(absl::Seconds(1))); - return absl::OkStatus(); - }, - - [](const absl::StatusOr& got) { - EXPECT_THAT(got.status(), - StatusIs(absl::StatusCode::kInvalidArgument)); - }, - }, - { - "complex_select_success", - "((false)? a.child.child : b.child).child.payload.single_int64", - {{"a", ""}, - {"b", - R"pb( - child { child { payload { single_int64: -42 } } } - )pb"}}, - ActivationSetupFn(), - - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), -42); - }, - }, - { - "chained_select_presence_present", - "has(nested_test_all_types.child.payload.standalone_message.bb)", - {{"nested_test_all_types", - R"pb( - child { payload { standalone_message { bb: 2 } } } - )pb"}}, - ActivationSetupFn(), - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_TRUE(result.GetBool().NativeValue()); - }, - }, - { - "chained_select_presence_not_present", - "has(nested_test_all_types.child.payload.standalone_message.bb)", - {{"nested_test_all_types", - R"pb( - child { payload { standalone_message {} } } - )pb"}}, - ActivationSetupFn(), - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_FALSE(result.GetBool().NativeValue()); - }, - }, - { - "select_with_map_supported", - "nested_test_all_types.payload.map_string_message['$not_a_field']." - "bb", - {{"nested_test_all_types", - R"pb( - payload { - map_string_message { - key: "$not_a_field", - value { bb: 5 } - } - } - )pb"}}, - ActivationSetupFn(), - - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 5); - }, - }, - { - "select_with_map_no_such_key", - "nested_test_all_types.payload.map_string_message['$not_a_field']." - "bb", - {{"nested_test_all_types", - R"pb( - payload { - map_string_message { - key: "a_different_field", - value { bb: 5 } - } - } - )pb"}}, - ActivationSetupFn(), - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_THAT(result.GetError().NativeValue(), - StatusIs(absl::StatusCode::kNotFound, - AllOf(HasSubstr("Key not found"), - HasSubstr("$not_a_field")))); - }, - }, - { - "select_with_repeated_supported", - "nested_test_all_types.payload.repeated_nested_message[1].bb", - {{"nested_test_all_types", - R"pb( - payload { - repeated_nested_message {} - repeated_nested_message { bb: 7 } - } - )pb"}}, - ActivationSetupFn(), - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 7); - }, - }, - { - "select_with_repeated_index_out_of_bounds", - "nested_test_all_types.payload.repeated_nested_message[1].bb", - {{"nested_test_all_types", - R"pb( - payload { repeated_nested_message {} } - )pb"}}, - ActivationSetupFn(), - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_THAT(result.GetError().NativeValue(), - StatusIs(absl::StatusCode::kInvalidArgument, - HasSubstr("index out of bounds"))); - }, - }, - { - // -2^32, which would read element 0 if truncated to an int. - "select_with_repeated_index_beyond_int32", - "nested_test_all_types.payload.repeated_nested_message" - "[-4294967296].bb", - {{"nested_test_all_types", - R"pb( - payload { repeated_nested_message {} } - )pb"}}, - ActivationSetupFn(), - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_THAT(result.GetError().NativeValue(), - StatusIs(absl::StatusCode::kInvalidArgument)); - }, - }, - { - "unknown_field", - "((false)? a.child.child : b.child).child.payload.single_int64", - {{"a", ""}, - {"b", - R"pb( - child { child { payload { single_int64: -42 } } } - )pb"}}, - [](google::protobuf::Arena*, Activation& act) { - return act.SetUnknownPatterns({AttributePattern( - "b", {AttributeQualifierPattern::OfString("child"), - AttributeQualifierPattern::OfString("child")})}); - }, - - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_THAT( - result.GetUnknown().ToAttributeSet(), - ElementsAre(Eq(Attribute( - "b", { - AttributeQualifier::OfString("child"), - AttributeQualifier::OfString("child"), - AttributeQualifier::OfString("payload"), - AttributeQualifier::OfString("single_int64"), - })))); - }, - }, - { - "unknown_field_partial", - "((false)? a.child.child : b.child).child.payload.single_int64", - {{"a", ""}, - {"b", - R"pb( - child { child { payload { single_int64: -42 } } } - )pb"}}, - [](google::protobuf::Arena*, Activation& act) { - return act.SetUnknownPatterns({AttributePattern( - "b", {AttributeQualifierPattern::OfString("child"), - AttributeQualifierPattern::OfString("child"), - AttributeQualifierPattern::OfString("child")})}); - }, - - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), -42); - }, - }, - { - "unknown_ident", - "((false)? a.child.child : b.child).child.payload.single_int64", - {{"a", ""}, - {"b", - R"pb( - child { child { payload { single_int64: -42 } } } - )pb"}}, - [](google::protobuf::Arena*, Activation& act) { - return act.SetUnknownPatterns({ - AttributePattern("b", {}), - }); - }, - - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_THAT(result.GetUnknown().ToAttributeSet(), - ElementsAre(Truly([](const Attribute& attr) { - return attr.variable_name() == "b"; - }))); - }, - }, - { - "unknown_pruned", - "((false)? a.child.child : b.child).child.payload.single_int64", - {{"a", ""}, - {"b", - R"pb( - child { child { payload { single_int64: -42 } } } - )pb"}}, - [](google::protobuf::Arena*, Activation& act) { - return act.SetUnknownPatterns({ - AttributePattern("a", {}), - }); - }, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), -42); - }, - }, - { - "missing_field", - "custom_predicate(nested_test_all_types.child.payload.standalone_" - "message)", - {{"nested_test_all_types", - R"pb( - child { payload { standalone_message { bb: 20 } } } - )pb"}}, - [](google::protobuf::Arena*, Activation& act) { - return act.SetMissingPatterns({AttributePattern( - "nested_test_all_types", - { - AttributeQualifierPattern::OfString("child"), - })}); - }, - - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_THAT(result.GetError().NativeValue().message(), + testing::Combine( + testing::ValuesIn( + { + { + "chained_select_success", + "nested_test_all_types.child.payload.standalone_message.bb", + {{"nested_test_all_types", + R"pb( + child { payload { standalone_message { bb: 42 } } } + )pb"}}, + ActivationSetupFn(), + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "chained_select_defaults_success", + "nested_test_all_types.child.payload.standalone_message.bb", + {{"nested_test_all_types", R"pb()pb"}}, + ActivationSetupFn(), + + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 0); + }, + }, + { + "chained_select_partial_success", + "nested_test_all_types.child.payload.standalone_message.bb", + {}, + [](google::protobuf::Arena* arena, Activation& act) { + auto* custom_struct = + google::protobuf::Arena::Create( + arena, IntValue(42)); + act.InsertOrAssignValue( + "nested_test_all_types", + CustomStructValue(custom_struct, arena)); + return absl::OkStatus(); + }, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "chained_select_presence_partial_present", + "has(nested_test_all_types.child.payload.standalone_" + "message." + "bb)", + {}, + [](google::protobuf::Arena* arena, Activation& act) { + auto* custom_struct = + google::protobuf::Arena::Create( + arena, IntValue(42)); + act.InsertOrAssignValue( + "nested_test_all_types", + CustomStructValue(custom_struct, arena)); + return absl::OkStatus(); + }, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "chained_select_not_bound", + "nested_test_all_types.child.payload.standalone_message.bb", + {}, // not set + ActivationSetupFn(), + + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kUnknown, + HasSubstr("nested_test_all_types"))); + }, + }, + { + // Some clients will use maps to represent a protobuf + // message at runtime. This is not yet supported. + "chained_select_map_as_root_unsupported", + "nested_test_all_types.child.payload.standalone_message.bb", + {}, // not set + [](google::protobuf::Arena* arena, Activation& act) -> absl::Status { + auto builder = cel::NewMapValueBuilder(arena); + CEL_RETURN_IF_ERROR( + builder->Put(cel::StringValue::WrapUnsafe("child"), + cel::NullValue())); + + auto value = std::move(*builder).Build(); + + act.InsertOrAssignValue("nested_test_all_types", + std::move(value)); + + return absl::OkStatus(); + }, + + [](const absl::StatusOr& got) { + EXPECT_THAT(got.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + }, + }, + { + // Some clients will use maps to represent a protobuf at + // runtime, this is not yet supported. + "chained_select_noncontainer_as_root_unsupported", + "nested_test_all_types.child.payload.standalone_message.bb", + {}, // not set + [](google::protobuf::Arena* arena, Activation& act) { + act.InsertOrAssignValue( + "nested_test_all_types", + cel::DurationValue(absl::Seconds(1))); + return absl::OkStatus(); + }, + + [](const absl::StatusOr& got) { + EXPECT_THAT(got.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); + }, + }, + { + "complex_select_success", + "((false)? a.child.child : " + "b.child).child.payload.single_int64", + {{"a", ""}, + {"b", + R"pb( + child { child { payload { single_int64: -42 } } } + )pb"}}, + ActivationSetupFn(), + + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), -42); + }, + }, + { + "chained_select_presence_present", + "has(nested_test_all_types.child.payload.standalone_" + "message." + "bb)", + {{"nested_test_all_types", + R"pb( + child { payload { standalone_message { bb: 2 } } } + )pb"}}, + ActivationSetupFn(), + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "chained_select_presence_not_present", + "has(nested_test_all_types.child.payload.standalone_" + "message." + "bb)", + {{"nested_test_all_types", + R"pb( + child { payload { standalone_message {} } } + )pb"}}, + ActivationSetupFn(), + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_map_supported", + "nested_test_all_types.payload.map_string_message['$not_a_" + "field']." + "bb", + {{"nested_test_all_types", + R"pb( + payload { + map_string_message { + key: "$not_a_field", + value { bb: 5 } + } + } + )pb"}}, + ActivationSetupFn(), + + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 5); + }, + }, + { + "select_with_map_no_such_key", + "nested_test_all_types.payload.map_string_message['$not_a_" + "field']." + "bb", + {{"nested_test_all_types", + R"pb( + payload { + map_string_message { + key: "a_different_field", + value { bb: 5 } + } + } + )pb"}}, + ActivationSetupFn(), + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kNotFound, + AllOf(HasSubstr("Key not found"), + HasSubstr("$not_a_field")))); + }, + }, + { + "select_with_repeated_supported", + "nested_test_all_types.payload.repeated_nested_message[1]." + "bb", + {{"nested_test_all_types", + R"pb( + payload { + repeated_nested_message {} + repeated_nested_message { bb: 7 } + } + )pb"}}, + ActivationSetupFn(), + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 7); + }, + }, + { + "select_with_repeated_index_out_of_bounds", + "nested_test_all_types.payload.repeated_nested_message[1]." + "bb", + {{"nested_test_all_types", + R"pb( + payload { repeated_nested_message {} } + )pb"}}, + ActivationSetupFn(), + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("index out of bounds"))); + }, + }, + { + // -2^32, which would read element 0 if truncated to an int. + "select_with_repeated_index_beyond_int32", + "nested_test_all_types.payload.repeated_nested_message" + "[-4294967296].bb", + {{"nested_test_all_types", + R"pb( + payload { repeated_nested_message {} } + )pb"}}, + ActivationSetupFn(), + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument)); + }, + }, + { + "unknown_field", + "((false)? a.child.child : " + "b.child).child.payload.single_int64", + {{"a", ""}, + {"b", + R"pb( + child { child { payload { single_int64: -42 } } } + )pb"}}, + [](google::protobuf::Arena*, Activation& act) { + return act.SetUnknownPatterns({AttributePattern( + "b", + {AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("child")})}); + }, + + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT( + result.GetUnknown().ToAttributeSet(), + ElementsAre(Eq(Attribute( + "b", + { + AttributeQualifier::OfString("child"), + AttributeQualifier::OfString("child"), + AttributeQualifier::OfString("payload"), + AttributeQualifier::OfString("single_int64"), + })))); + }, + }, + { + "unknown_field_partial", + "((false)? a.child.child : " + "b.child).child.payload.single_int64", + {{"a", ""}, + {"b", + R"pb( + child { child { payload { single_int64: -42 } } } + )pb"}}, + [](google::protobuf::Arena*, Activation& act) { + return act.SetUnknownPatterns({AttributePattern( + "b", + {AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("child")})}); + }, + + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), -42); + }, + }, + { + "unknown_ident", + "((false)? a.child.child : " + "b.child).child.payload.single_int64", + {{"a", ""}, + {"b", + R"pb( + child { child { payload { single_int64: -42 } } } + )pb"}}, + [](google::protobuf::Arena*, Activation& act) { + return act.SetUnknownPatterns({ + AttributePattern("b", {}), + }); + }, + + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetUnknown().ToAttributeSet(), + ElementsAre(Truly([](const Attribute& attr) { + return attr.variable_name() == "b"; + }))); + }, + }, + { + "unknown_pruned", + "((false)? a.child.child : " + "b.child).child.payload.single_int64", + {{"a", ""}, + {"b", + R"pb( + child { child { payload { single_int64: -42 } } } + )pb"}}, + [](google::protobuf::Arena*, Activation& act) { + return act.SetUnknownPatterns({ + AttributePattern("a", {}), + }); + }, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), -42); + }, + }, + { + "missing_field", + "custom_predicate(nested_test_all_types.child.payload." + "standalone_" + "message)", + {{"nested_test_all_types", + R"pb( + child { payload { standalone_message { bb: 20 } } } + )pb"}}, + [](google::protobuf::Arena*, Activation& act) { + return act.SetMissingPatterns({AttributePattern( + "nested_test_all_types", + { + AttributeQualifierPattern::OfString("child"), + })}); + }, + + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT( + result.GetError().NativeValue().message(), HasSubstr("nested_test_all_types.child.payload." "standalone_message")); - }, - }, - { - "missing_field_partial", - "custom_predicate(nested_test_all_types.child.payload.standalone_" - "message)", - {{"nested_test_all_types", - R"pb( - child { payload { standalone_message { bb: 20 } } } - )pb"}}, - [](google::protobuf::Arena*, Activation& act) { - return act.SetMissingPatterns({AttributePattern( - "b", {AttributeQualifierPattern::OfString("child"), - AttributeQualifierPattern::OfString("child")})}); - }, - - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_TRUE(result.GetBool().NativeValue()); - }, - }, - { - "select_wrapper_int_leaf", - "nested_test_all_types.payload.single_int64_wrapper", - {{"nested_test_all_types", - R"pb( - payload { single_int64_wrapper { value: 10 } } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 10); - }, - }, - { - "select_repeated_leaf", - "nested_test_all_types.payload.repeated_int64", - {{"nested_test_all_types", - R"pb( - payload { repeated_int64: 10 } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - }, - }, - { - "select_map_leaf", - "nested_test_all_types.payload.map_string_int64", - {{"nested_test_all_types", - R"pb( - payload { map_string_int64 { key: "key", value: 12 } } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - }, - }, - { - "select_with_map_dot", - "nested_test_all_types.payload.map_string_message.field_like_key." - "bb", - {{"nested_test_all_types", - R"pb( - payload { - map_string_message { - key: "field_like_key", - value { bb: 42 } - } - } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 42); - }, - }, - { - "select_with_map_bool", - "nested_test_all_types.payload.map_bool_message[false].bb", - {{"nested_test_all_types", - R"pb( - payload { - map_bool_message { - key: false, - value { bb: 42 } - } - } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 42); - }, - }, - { - "select_with_map_int", - "nested_test_all_types.payload.map_int64_message[-1].bb", - {{"nested_test_all_types", - R"pb( - payload { - map_int64_message { - key: -1, - value { bb: 42 } - } - } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 42); - }, - }, - { - "select_with_map_uint", - "nested_test_all_types.payload.map_uint64_message[1u].bb", - {{"nested_test_all_types", - R"pb( - payload { - map_uint64_message { - key: 1, - value { bb: 42 } - } - } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 42); - }, - }, - { - "select_with_repeated", - "nested_test_all_types.payload.repeated_nested_message[1].bb", - {{"nested_test_all_types", - R"pb( - payload { - repeated_nested_message {} - repeated_nested_message { bb: 42 } - } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 42); - }, - }, - { - "select_with_any", - "nested_test_all_types.payload.single_any.single_int64", - {{"nested_test_all_types", - R"pb( - payload { - single_any { - [type.googleapis.com/cel.expr.conformance.proto2 - .TestAllTypes] { single_int64: 42 } - } - } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_EQ(result.GetInt().NativeValue(), 42); - }, - }, - { - "has_repeated_leaf_true", - "has(nested_test_all_types.payload.repeated_int64)", - {{"nested_test_all_types", - R"pb( - payload { repeated_int64: 42 } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_TRUE(result.GetBool().NativeValue()); - }, - }, - { - "has_repeated_leaf_false", - "has(nested_test_all_types.payload.repeated_int64)", - {{"nested_test_all_types", - R"pb( - payload {} - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_FALSE(result.GetBool().NativeValue()); - }, - }, - { - "has_map_leaf_true", - "has(nested_test_all_types.payload.map_string_int64)", - {{"nested_test_all_types", - R"pb( - payload { map_string_int64 { key: "string" value: 12 } } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_TRUE(result.GetBool().NativeValue()); - }, - }, - { - "has_map_leaf_false", - "has(nested_test_all_types.payload.map_string_int64)", - {{"nested_test_all_types", - R"pb( - payload {} - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_FALSE(result.GetBool().NativeValue()); - }, - }, - { - "has_map_field_like_key", - "has(nested_test_all_types.payload.map_string_int64.field_like_" - "key)", - {{"nested_test_all_types", - R"pb( - payload { map_string_int64 { key: "field_like_key" value: 12 } } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_TRUE(result.GetBool().NativeValue()); - }, - }, - { - "has_map_field_like_key_false", - "has(nested_test_all_types.payload.map_string_int64.field_like_" - "key)", - {{"nested_test_all_types", - R"pb( - payload { map_string_int64 { key: "wrong_key" value: 12 } } - )pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_FALSE(result.GetBool().NativeValue()); - }, - }, - { - "select_wrong_runtime_type", - "test_all_types.single_int64", - {{}}, - [](google::protobuf::Arena* arena, Activation& activation) { - activation.InsertOrAssignValue("test_all_types", - cel::IntValue(42)); - return absl::OkStatus(); - }, - [](const absl::StatusOr& got) { - EXPECT_THAT(got, StatusIs(absl::StatusCode::kInvalidArgument, - HasSubstr("Expected struct type"))); - }, - }, - { - "select_with_struct", - "nested_test_all_types.payload.single_struct['key']['subkey']", - {{"nested_test_all_types", - R"pb(payload { - single_struct { - fields { - key: "key" - value { - struct_value { - fields { - key: "subkey" + }, + }, + { + "missing_field_partial", + "custom_predicate(nested_test_all_types.child.payload." + "standalone_" + "message)", + {{"nested_test_all_types", + R"pb( + child { payload { standalone_message { bb: 20 } } } + )pb"}}, + [](google::protobuf::Arena*, Activation& act) { + return act.SetMissingPatterns({AttributePattern( + "b", + {AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("child")})}); + }, + + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_wrapper_int_leaf", + "nested_test_all_types.payload.single_int64_wrapper", + {{"nested_test_all_types", + R"pb( + payload { single_int64_wrapper { value: 10 } } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 10); + }, + }, + { + "select_repeated_leaf", + "nested_test_all_types.payload.repeated_int64", + {{"nested_test_all_types", + R"pb( + payload { repeated_int64: 10 } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + }, + }, + { + "select_map_leaf", + "nested_test_all_types.payload.map_string_int64", + {{"nested_test_all_types", + R"pb( + payload { map_string_int64 { key: "key", value: 12 } } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + }, + }, + { + "select_with_map_dot", + "nested_test_all_types.payload.map_string_message.field_" + "like_" + "key." + "bb", + {{"nested_test_all_types", + R"pb( + payload { + map_string_message { + key: "field_like_key", + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "select_with_map_bool", + "nested_test_all_types.payload.map_bool_message[false].bb", + {{"nested_test_all_types", + R"pb( + payload { + map_bool_message { + key: false, + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "select_with_map_int", + "nested_test_all_types.payload.map_int64_message[-1].bb", + {{"nested_test_all_types", + R"pb( + payload { + map_int64_message { + key: -1, + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "select_with_map_uint", + "nested_test_all_types.payload.map_uint64_message[1u].bb", + {{"nested_test_all_types", + R"pb( + payload { + map_uint64_message { + key: 1, + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "select_with_repeated", + "nested_test_all_types.payload.repeated_nested_message[1]." + "bb", + {{"nested_test_all_types", + R"pb( + payload { + repeated_nested_message {} + repeated_nested_message { bb: 42 } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "select_with_any", + "nested_test_all_types.payload.single_any.single_int64", + {{"nested_test_all_types", + R"pb( + payload { + single_any { + [type.googleapis.com/cel.expr.conformance.proto2 + .TestAllTypes] { single_int64: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "has_repeated_leaf_true", + "has(nested_test_all_types.payload.repeated_int64)", + {{"nested_test_all_types", + R"pb( + payload { repeated_int64: 42 } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_repeated_leaf_false", + "has(nested_test_all_types.payload.repeated_int64)", + {{"nested_test_all_types", + R"pb( + payload {} + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "has_map_leaf_true", + "has(nested_test_all_types.payload.map_string_int64)", + {{"nested_test_all_types", + R"pb( + payload { map_string_int64 { key: "string" value: 12 } } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_map_leaf_false", + "has(nested_test_all_types.payload.map_string_int64)", + {{"nested_test_all_types", + R"pb( + payload {} + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "has_map_field_like_key", + "has(nested_test_all_types.payload.map_string_int64.field_" + "like_" + "key)", + {{"nested_test_all_types", + R"pb( + payload { + map_string_int64 { key: "field_like_key" value: 12 } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_map_field_like_key_false", + "has(nested_test_all_types.payload.map_string_int64.field_" + "like_" + "key)", + {{"nested_test_all_types", + R"pb( + payload { + map_string_int64 { key: "wrong_key" value: 12 } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "has_simple_true", + "has(nested_test_all_types.child)", + {{"nested_test_all_types", + R"pb( + child {} + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_simple_false", + "has(nested_test_all_types.child)", + {{"nested_test_all_types", R"pb()pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "has_through_map_true", + "has(nested_test_all_types.payload.map_string_message['key'" + "]." + "bb)", + {{"nested_test_all_types", + R"pb( + payload { + map_string_message { + key: "key" + value { bb: 5 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_through_map_false", + "has(nested_test_all_types.payload.map_string_message['key'" + "]." + "bb)", + {{"nested_test_all_types", + R"pb( + payload { + map_string_message { + key: "key" + value {} + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "has_through_map_no_such_key", + "has(nested_test_all_types.payload.map_string_message['" + "missing_" + "key'].bb)", + {{"nested_test_all_types", + R"pb( + payload { + map_string_message { + key: "other_key" + value { bb: 5 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kNotFound, + AllOf(HasSubstr("Key not found"), + HasSubstr("missing_key")))); + }, + }, + { + "has_through_map_int32_overflow", + "has(nested_test_all_types.payload.map_int32_message[" + "2147483648].bb)", + {{"nested_test_all_types", + R"pb( + payload { + map_int32_message { + key: 1 + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT( + result.GetError().NativeValue(), + testing::AnyOf( + StatusIs(absl::StatusCode::kOutOfRange, + HasSubstr("integer overflow")), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type")))); + }, + }, + { + "has_through_repeated_true", + "has(nested_test_all_types.payload.repeated_nested_message[" + "1]." + "bb)", + {{"nested_test_all_types", + R"pb( + payload { + repeated_nested_message {} + repeated_nested_message { bb: 7 } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_through_repeated_false", + "has(nested_test_all_types.payload.repeated_nested_message[" + "1]." + "bb)", + {{"nested_test_all_types", + R"pb( + payload { + repeated_nested_message {} + repeated_nested_message {} + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "has_through_repeated_index_out_of_bounds", + "has(nested_test_all_types.payload.repeated_nested_message[" + "1]." + "bb)", + {{"nested_test_all_types", + R"pb( + payload { repeated_nested_message { bb: 7 } } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("index out of bounds"))); + }, + }, + { + "has_through_repeated_negative_index", + "has(nested_test_all_types.payload.repeated_nested_message[" + "-1]." + "bb)", + {{"nested_test_all_types", + R"pb( + payload { repeated_nested_message { bb: 7 } } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument)); + }, + }, + { + "select_wrong_runtime_type", + "test_all_types.single_int64", + {{}}, + [](google::protobuf::Arena* arena, Activation& activation) { + activation.InsertOrAssignValue("test_all_types", + cel::IntValue(42)); + return absl::OkStatus(); + }, + [](const absl::StatusOr& got) { + EXPECT_THAT(got, + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Expected struct type"))); + }, + }, + { + "select_with_struct", + "nested_test_all_types.payload.single_struct['key']['" + "subkey']", + {{"nested_test_all_types", + R"pb(payload { + single_struct { + fields { + key: "key" + value { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_list_value", + "nested_test_all_types.payload.list_value[0]['subkey']", + {{"nested_test_all_types", + R"pb(payload { + list_value { + values { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_value", + "nested_test_all_types.payload.single_value['key']['subkey'" + "]", + {{"nested_test_all_types", + R"pb(payload { + single_value { + struct_value { + fields { + key: "key" + value { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_map_int32", + "nested_test_all_types.payload.map_int32_message[-1].bb", + {{"nested_test_all_types", + R"pb( + payload { + map_int32_message { + key: -1, + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "select_with_map_int32_overflow", + "nested_test_all_types.payload.map_int32_message[" + "2147483648]." + "bb", + {{"nested_test_all_types", + R"pb( + payload { + map_int32_message { + key: 1, + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT( + result.GetError().NativeValue(), + testing::AnyOf( + StatusIs(absl::StatusCode::kOutOfRange, + HasSubstr("integer overflow")), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type")))); + }, + }, + { + "select_with_map_int32_underflow", + "nested_test_all_types.payload.map_int32_message[-" + "2147483649]." + "bb", + {{"nested_test_all_types", + R"pb( + payload { + map_int32_message { + key: -1, + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT( + result.GetError().NativeValue(), + testing::AnyOf( + StatusIs(absl::StatusCode::kOutOfRange, + HasSubstr("integer overflow")), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type")))); + }, + }, + { + "select_with_map_uint32", + "nested_test_all_types.payload.map_uint32_message[1u].bb", + {{"nested_test_all_types", + R"pb( + payload { + map_uint32_message { + key: 1, + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "select_with_map_uint32_overflow", + "nested_test_all_types.payload.map_uint32_message[" + "4294967296u]." + "bb", + {{"nested_test_all_types", + R"pb( + payload { + map_uint32_message { + key: 1, + value { bb: 42 } + } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT( + result.GetError().NativeValue(), + testing::AnyOf( + StatusIs(absl::StatusCode::kOutOfRange, + HasSubstr("unsigned integer over")), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type")))); + }, + }, + { + "select_map_element_leaf", + "nested_test_all_types.payload.map_string_int64['key']", + {{"nested_test_all_types", + R"pb( + payload { map_string_int64 { key: "key", value: 12 } } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 12); + }, + }, + { + "select_map_dot_leaf", + "nested_test_all_types.payload.map_string_int64.field_like_" + "key", + {{"nested_test_all_types", + R"pb( + payload { + map_string_int64 { key: "field_like_key", value: 12 } + } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 12); + }, + }, + { + "select_repeated_element_leaf", + "nested_test_all_types.payload.repeated_int64[1]", + {{"nested_test_all_types", + R"pb( + payload { repeated_int64: 10 repeated_int64: 42 } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 42); + }, + }, + { + "select_with_repeated_negative_index", + "nested_test_all_types.payload.repeated_nested_message[-1]." + "bb", + {{"nested_test_all_types", + R"pb( + payload { repeated_nested_message { bb: 42 } } + )pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument)); + }, + }, + { + "select_with_map_struct", + "nested_test_all_types.payload.map_string_struct['key']['" + "subkey']", + {{"nested_test_all_types", + R"pb(payload { + map_string_struct { + key: "key" + value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_map_list_value", + "nested_test_all_types.payload.map_string_list_value['key']" + "[0][" + "'subkey']", + {{"nested_test_all_types", + R"pb(payload { + map_string_list_value { + key: "key" + value { + values { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_map_value", + "nested_test_all_types.payload.map_string_value['key']['" + "subkey'" + "]", + {{"nested_test_all_types", + R"pb(payload { + map_string_value { + key: "key" + value { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_repeated_struct", + "nested_test_all_types.payload.repeated_struct[0]['subkey'" + "]", + {{"nested_test_all_types", + R"pb(payload { + repeated_struct { + fields { + key: "subkey" + value { bool_value: true } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_repeated_list_value", + "nested_test_all_types.payload.repeated_list_value[0][0]['" + "subkey']", + {{"nested_test_all_types", + R"pb(payload { + repeated_list_value { + values { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_repeated_value", + "nested_test_all_types.payload.repeated_value[0]['subkey']", + {{"nested_test_all_types", + R"pb(payload { + repeated_value { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_bad_any_fallback", + "nested_test_all_types.payload.single_any['subkey']", + {{"nested_test_all_types", + R"pb(payload { + single_any { + type_url: "type.googleapis.com/UnknownType" + value: "invalid" + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + }, + }, + { + "select_struct_leaf", + "nested_test_all_types.payload.single_struct", + {{"nested_test_all_types", + R"pb(payload { + single_struct { + fields { + key: "key" + value { bool_value: true } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetMap().Size(), + absl_testing::IsOkAndHolds(1)); + }, + }, + { + "select_struct_leaf_unset", + "nested_test_all_types.payload.single_struct", + {{"nested_test_all_types", R"pb(payload {})pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetMap().Size(), + absl_testing::IsOkAndHolds(0)); + }, + }, + { + "select_value_leaf", + "nested_test_all_types.payload.single_value", + {{"nested_test_all_types", + R"pb(payload { single_value { bool_value: true } })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_value_leaf_unset", + "nested_test_all_types.payload.single_value", + {{"nested_test_all_types", R"pb(payload {})pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + EXPECT_TRUE(result->Is()) + << result->DebugString(); + }, + }, + { + "select_list_value_leaf", + "nested_test_all_types.payload.list_value", + {{"nested_test_all_types", + R"pb(payload { + list_value { values { bool_value: true } } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetList().Size(), + absl_testing::IsOkAndHolds(1)); + }, + }, + { + "select_list_value_leaf_unset", + "nested_test_all_types.payload.list_value", + {{"nested_test_all_types", R"pb(payload {})pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetList().Size(), + absl_testing::IsOkAndHolds(0)); + }, + }, + { + "has_struct_leaf_true", + "has(nested_test_all_types.payload.single_struct)", + {{"nested_test_all_types", + R"pb(payload { single_struct {} })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_struct_leaf_false", + "has(nested_test_all_types.payload.single_struct)", + {{"nested_test_all_types", R"pb(payload {})pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "has_value_leaf_true", + "has(nested_test_all_types.payload.single_value)", + {{"nested_test_all_types", R"pb(payload { + single_value { + null_value: NULL_VALUE + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_value_leaf_false", + "has(nested_test_all_types.payload.single_value)", + {{"nested_test_all_types", R"pb(payload {})pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "has_list_value_leaf_true", + "has(nested_test_all_types.payload.list_value)", + {{"nested_test_all_types", + R"pb(payload { list_value {} })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_list_value_leaf_false", + "has(nested_test_all_types.payload.list_value)", + {{"nested_test_all_types", R"pb(payload {})pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "select_map_struct_leaf", + "nested_test_all_types.payload.map_string_struct['key']", + {{"nested_test_all_types", + R"pb(payload { + map_string_struct { + key: "key" + value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetMap().Size(), + absl_testing::IsOkAndHolds(1)); + }, + }, + { + "select_map_list_value_leaf", + "nested_test_all_types.payload.map_string_list_value['key'" + "]", + {{"nested_test_all_types", + R"pb(payload { + map_string_list_value { + key: "key" + value { values { bool_value: true } } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetList().Size(), + absl_testing::IsOkAndHolds(1)); + }, + }, + { + "select_map_value_leaf", + "nested_test_all_types.payload.map_string_value['key']", + {{"nested_test_all_types", + R"pb(payload { + map_string_value { + key: "key" value { bool_value: true } } - } - } - } - } - })pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_TRUE(result.GetBool().NativeValue()); - }, - }, - { - "select_with_list_value", - "nested_test_all_types.payload.list_value[0]['subkey']", - {{"nested_test_all_types", - R"pb(payload { - list_value { - values { - struct_value { - fields { - key: "subkey" - value { bool_value: true } - } - } - } - } - })pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_TRUE(result.GetBool().NativeValue()); - }, - }, - { - "select_with_value", - "nested_test_all_types.payload.single_value['key']['subkey']", - {{"nested_test_all_types", - R"pb(payload { - single_value { - struct_value { - fields { - key: "key" - value { - struct_value { + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_repeated_struct_leaf", + "nested_test_all_types.payload.repeated_struct[0]", + {{"nested_test_all_types", + R"pb(payload { + repeated_struct { fields { key: "subkey" value { bool_value: true } } } - } - } - } - } - })pb"}}, - nullptr, - [](const absl::StatusOr& got) { - ASSERT_OK_AND_ASSIGN(Value result, got); - ASSERT_TRUE(result->Is()) << result->DebugString(); - EXPECT_TRUE(result.GetBool().NativeValue()); - }, - }, - }), - - [](const testing::TestParamInfo& info) { - return info.param.case_name; + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetMap().Size(), + absl_testing::IsOkAndHolds(1)); + }, + }, + { + "select_repeated_list_value_leaf", + "nested_test_all_types.payload.repeated_list_value[0]", + {{"nested_test_all_types", + R"pb(payload { + repeated_list_value { values { bool_value: true } } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_THAT(result.GetList().Size(), + absl_testing::IsOkAndHolds(1)); + }, + }, + { + "select_repeated_value_leaf", + "nested_test_all_types.payload.repeated_value[0]", + {{"nested_test_all_types", R"pb(payload { + repeated_value { + bool_value: true + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_struct_dot", + "nested_test_all_types.payload.single_struct.key.subkey", + {{"nested_test_all_types", + R"pb(payload { + single_struct { + fields { + key: "key" + value { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_with_struct_dot_true", + "has(nested_test_all_types.payload.single_struct.key)", + {{"nested_test_all_types", + R"pb(payload { + single_struct { + fields { + key: "key" + value { bool_value: false } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_with_struct_dot_false", + "has(nested_test_all_types.payload.single_struct.key)", + {{"nested_test_all_types", + R"pb(payload { single_struct {} })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "has_with_struct_nested_dot_true", + "has(nested_test_all_types.payload.single_struct.key." + "subkey)", + {{"nested_test_all_types", + R"pb(payload { + single_struct { + fields { + key: "key" + value { + struct_value { + fields { + key: "subkey" + value { bool_value: false } + } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_with_struct_nested_dot_false", + "has(nested_test_all_types.payload.single_struct.key." + "subkey)", + {{"nested_test_all_types", + R"pb(payload { + single_struct { + fields { + key: "key" + value { struct_value {} } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_value_dot", + "nested_test_all_types.payload.single_value.key.subkey", + {{"nested_test_all_types", + R"pb(payload { + single_value { + struct_value { + fields { + key: "key" + value { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_with_value_dot_true", + "has(nested_test_all_types.payload.single_value.key)", + {{"nested_test_all_types", + R"pb(payload { + single_value { + struct_value { + fields { + key: "key" + value { bool_value: false } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + { + "has_with_value_dot_false", + "has(nested_test_all_types.payload.single_value.key)", + {{"nested_test_all_types", + R"pb(payload { single_value { struct_value {} } })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_FALSE(result.GetBool().NativeValue()); + }, + }, + { + "select_with_list_value_dot", + "nested_test_all_types.payload.list_value[0].subkey", + {{"nested_test_all_types", + R"pb(payload { + list_value { + values { + struct_value { + fields { + key: "subkey" + value { bool_value: true } + } + } + } + } + })pb"}}, + nullptr, + [](const absl::StatusOr& got) { + ASSERT_OK_AND_ASSIGN(Value result, got); + ASSERT_TRUE(result->Is()) + << result->DebugString(); + EXPECT_TRUE(result.GetBool().NativeValue()); + }, + }, + }), + testing::Bool()), + [](const testing::TestParamInfo< + SelectOptimizationProgramOptimizerTest::ParamType>& info) { + return absl::StrCat(std::get<0>(info.param).case_name, "_", + std::get<1>(info.param) ? "cached" : "uncached"); }); // Tests covering unexpected / malformed ASTs. @@ -1831,6 +3255,18 @@ class SelectOptimizationUnexpectedAstTest : public SelectOptimizationTest { return result; } + Expr NextUint(uint64_t value) { + Expr result = NextExpr(); + result.mutable_const_expr().set_uint64_value(value); + return result; + } + + Expr NextBool(bool value) { + Expr result = NextExpr(); + result.mutable_const_expr().set_bool_value(value); + return result; + } + // Plans `fn(ident, args...)` with only the select program optimizer. template absl::Status PlanSelectCall(absl::string_view fn, Exprs... args) { @@ -1844,6 +3280,41 @@ class SelectOptimizationUnexpectedAstTest : public SelectOptimizationTest { return builder.CreateExpressionImpl(std::move(ast), nullptr).status(); } + // Helper for testing malformed / type mismatched optimized select chains. + absl::StatusOr EvalTypedSelectCall(absl::string_view fn, + Expr select_list, + const NestedTestAllTypes& msg) { + auto ast = std::make_unique(NextExpr(), SourceInfo()); + CallExpr& call = ast->mutable_root_expr().mutable_call_expr(); + call.set_function(fn); + Expr ident = NextExpr(); + int64_t ident_id = ident.id(); + ident.mutable_ident_expr().set_name("ident"); + call.mutable_args().push_back(std::move(ident)); + call.mutable_args().push_back(std::move(select_list)); + ast->mutable_type_map()[ident_id] = TypeSpec( + MessageTypeSpec("cel.expr.conformance.proto2.NestedTestAllTypes")); + + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = true; + FlatExprBuilder builder(env_, runtime_options_); + builder.AddProgramOptimizer( + CreateSelectOptimizationProgramOptimizer(select_options)); + CEL_ASSIGN_OR_RETURN(FlatExpression plan, + builder.CreateExpressionImpl(std::move(ast), nullptr)); + + cel::Activation act; + google::protobuf::Message* var = + runtime_internal::MakeTestingEnvDynamicProto(msg, &arena_); + act.InsertOrAssignValue("ident", cel::ParsedMessageValue(var, &arena_)); + google::api::expr::runtime::FlatExpressionEvaluatorState state = + plan.MakeEvaluatorState(env_->descriptor_pool.get(), + env_->MutableMessageFactory(), &arena_); + return plan.EvaluateWithCallback( + act, /*embedder_context=*/nullptr, + google::api::expr::runtime::EvaluationListener(), state); + } + protected: int64_t next_id_; }; @@ -2040,5 +3511,275 @@ TEST_F(SelectOptimizationUnexpectedAstTest, HasSubstr("Invalid cel.attribute select list"))); } +TEST_F(SelectOptimizationUnexpectedAstTest, CachedFieldDescriptorMapKeyErrors) { + NestedTestAllTypes msg; + + // map (field 63) with int key + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelAttribute, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(63), NextString("map_bool_bool")), + NextInt(1)), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type"))); + } + + // map (field 77) with string key + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelAttribute, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(77), NextString("map_int32_int32")), + NextString("bad_key")), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type"))); + } + + // map (field 89) with bool key + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelAttribute, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(89), NextString("map_int64_int64")), + NextBool(true)), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type"))); + } + + // map (field 201) with int key + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelAttribute, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(201), NextString("map_uint32_uint32")), + NextInt(1)), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type"))); + } + + // map (field 213) with int key + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelAttribute, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(213), NextString("map_uint64_uint64")), + NextInt(1)), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type"))); + } + + // map (field 61) with int key + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelAttribute, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(61), NextString("map_string_string")), + NextInt(1)), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type"))); + } +} + +TEST_F(SelectOptimizationUnexpectedAstTest, + CachedFieldDescriptorMapPresenceErrors) { + NestedTestAllTypes msg; + + // has(ident.payload.map_int64_int64[1]) -> non-string qualifier for has() + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelHasField, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(89), NextString("map_int64_int64")), + NextInt(1)), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kUnknown, + HasSubstr("No matching overloads found : has"))); + } + + // has(ident.payload.map_int64_int64.key) -> string qualifier on int64 map + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelHasField, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(89), NextString("map_int64_int64")), + NextString("key")), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invalid map key type"))); + } +} + +TEST_F(SelectOptimizationUnexpectedAstTest, CachedFieldDescriptorListErrors) { + NestedTestAllTypes msg; + + // has(ident.payload.repeated_int32[0]) -> presence test on list element + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelHasField, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(31), NextString("repeated_int32")), + NextInt(0)), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kUnknown, + HasSubstr("No matching overloads found : has"))); + } + + // ident.payload.repeated_int32['bad_index'] -> non-int64 list index + { + ASSERT_OK_AND_ASSIGN( + Value result, + EvalTypedSelectCall( + kCelAttribute, + NextList(NextList(NextInt(2), NextString("payload")), + NextList(NextInt(31), NextString("repeated_int32")), + NextString("bad_index")), + msg)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_THAT(result.GetError().NativeValue(), + StatusIs(absl::StatusCode::kUnknown, + HasSubstr("No matching overloads found : _[_]"))); + } +} + +// Tests for overlay pool with cross-pool extensions. +// We specifically recommend against doing this, but some clients do it. +// Behavior is not totally consistent with normal select implementation so +// this test is just confirming we don't crash or return garbage. +TEST_F(SelectOptimizationTest, + CachedFieldDescriptorCrossPoolExtensionFallback) { + auto overlay_pool = std::make_shared( + google::protobuf::DescriptorPool::generated_pool()); + ASSERT_NE( + overlay_pool->BuildFile(test::GetTestExternalExtensionsFileDescriptor()), + nullptr); + + CompilerOptions compiler_options; + compiler_options.parser_options.enable_quoted_identifiers = true; + ASSERT_OK_AND_ASSIGN(std::unique_ptr compiler_builder, + cel::NewCompilerBuilder(overlay_pool, compiler_options)); + ASSERT_THAT(compiler_builder->AddLibrary(cel::StandardCompilerLibrary()), + IsOk()); + const google::protobuf::Descriptor* desc = overlay_pool->FindMessageTypeByName( + "cel.expr.conformance.proto2.NestedTestAllTypes"); + ASSERT_NE(desc, nullptr); + ASSERT_THAT(compiler_builder->GetCheckerBuilder().AddVariable( + MakeVariableDecl("ident", MessageType(desc))), + IsOk()); + ASSERT_OK_AND_ASSIGN(std::unique_ptr compiler, + compiler_builder->Build()); + ASSERT_OK_AND_ASSIGN( + cel::ValidationResult validation, + compiler->Compile( + "ident.payload.`cel.cpp.testutil.test_external_extensions_message`." + "int64_field")); + ASSERT_TRUE(validation.IsValid()) << validation.FormatError(); + ASSERT_OK_AND_ASSIGN(std::unique_ptr ast, validation.ReleaseAst()); + + ASSERT_OK_AND_ASSIGN( + cel::RuntimeBuilder runtime_builder, + cel::CreateStandardRuntimeBuilder(overlay_pool, runtime_options_)); + SelectOptimizationOptions select_options; + select_options.enable_field_descriptor_caching = true; + ASSERT_THAT(EnableSelectOptimization(runtime_builder, select_options), + IsOk()); + ASSERT_OK_AND_ASSIGN(std::unique_ptr runtime, + std::move(runtime_builder).Build()); + ASSERT_OK_AND_ASSIGN(std::unique_ptr program, + runtime->CreateProgram(std::move(ast))); + + const google::protobuf::FieldDescriptor* payload_field = + desc->FindFieldByName("payload"); + ASSERT_NE(payload_field, nullptr); + const google::protobuf::FieldDescriptor* ext_field = overlay_pool->FindExtensionByName( + "cel.cpp.testutil.test_external_extensions_message"); + ASSERT_NE(ext_field, nullptr); + const google::protobuf::FieldDescriptor* int64_field = + ext_field->message_type()->FindFieldByName("int64_field"); + ASSERT_NE(int64_field, nullptr); + + google::protobuf::DynamicMessageFactory overlay_factory(overlay_pool.get()); + google::protobuf::Message* msg = overlay_factory.GetPrototype(desc)->New(&arena_); + google::protobuf::Message* payload = msg->GetReflection()->MutableMessage( + msg, payload_field, &overlay_factory); + google::protobuf::Message* ext_msg = payload->GetReflection()->MutableMessage( + payload, ext_field, &overlay_factory); + ext_msg->GetReflection()->SetInt64(ext_msg, int64_field, 99); + + // This works for the SelectOptimization, but not normal field access. Here, + // we are able to resolve the extension using the message reflection extension + // table (by number) without having to look up against the descriptor pool + // directly. + cel::Activation act; + act.InsertOrAssignValue("ident", cel::ParsedMessageValue(msg, &arena_)); + cel::EvaluateOptions eval_options; + eval_options.message_factory = &overlay_factory; + ASSERT_OK_AND_ASSIGN(Value result, + program->Evaluate(&arena_, act, eval_options)); + ASSERT_TRUE(result->Is()) << result->DebugString(); + EXPECT_EQ(result.GetInt().NativeValue(), 99); + + // Also test when the extension message is unset, where Reflection::GetMessage + // has to construct the default message instance. + google::protobuf::Message* unset_msg = overlay_factory.GetPrototype(desc)->New(&arena_); + act.InsertOrAssignValue("ident", cel::ParsedMessageValue(unset_msg, &arena_)); + ASSERT_OK_AND_ASSIGN(Value unset_result, + program->Evaluate(&arena_, act, eval_options)); + ASSERT_TRUE(unset_result->Is()) << unset_result->DebugString(); + EXPECT_EQ(unset_result.GetInt().NativeValue(), 0); + + // Also test when `payload` comes from a different MessageFactory (the + // generated factory) while `runtime` uses its default DynamicMessageFactory. + NestedTestAllTypes* empty_generated = + NestedTestAllTypes::default_instance().New(&arena_); + act.InsertOrAssignValue("ident", + cel::ParsedMessageValue(empty_generated, &arena_)); + ASSERT_OK_AND_ASSIGN(Value underlay_result, program->Evaluate(&arena_, act)); + EXPECT_TRUE(underlay_result->Is()) + << underlay_result->DebugString(); +} + } // namespace } // namespace cel::extensions