From 05556a07a062b8063d34086850b23f3b415c0fe1 Mon Sep 17 00:00:00 2001 From: Jonathan Tatum Date: Fri, 10 Jul 2026 11:03:21 -0700 Subject: [PATCH] Add wrapping version of BindProtoToActivation. PiperOrigin-RevId: 945797968 --- .../protobuf/bind_proto_to_activation.h | 1 + runtime/BUILD | 2 +- runtime/bind_proto_to_activation.h | 89 ++++++++++++++----- runtime/bind_proto_to_activation_test.cc | 34 +++++++ 4 files changed, 104 insertions(+), 22 deletions(-) diff --git a/extensions/protobuf/bind_proto_to_activation.h b/extensions/protobuf/bind_proto_to_activation.h index 97dfc6c0c..93025185b 100644 --- a/extensions/protobuf/bind_proto_to_activation.h +++ b/extensions/protobuf/bind_proto_to_activation.h @@ -25,6 +25,7 @@ using BindProtoUnsetFieldBehavior ABSL_DEPRECATED( ::cel::BindProtoUnsetFieldBehavior; using ::cel::BindProtoToActivation; +using ::cel::BindProtoViewToActivation; namespace protobuf_internal { diff --git a/runtime/BUILD b/runtime/BUILD index da2192439..6831aeaae 100644 --- a/runtime/BUILD +++ b/runtime/BUILD @@ -116,10 +116,10 @@ cc_library( hdrs = ["bind_proto_to_activation.h"], deps = [ ":activation", - "//common:casting", "//common:value", "//internal:status_macros", "@com_google_absl//absl/base:nullability", + "@com_google_absl//absl/log:absl_check", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", diff --git a/runtime/bind_proto_to_activation.h b/runtime/bind_proto_to_activation.h index 9bc866937..03476b386 100644 --- a/runtime/bind_proto_to_activation.h +++ b/runtime/bind_proto_to_activation.h @@ -18,9 +18,9 @@ #include #include "absl/base/nullability.h" +#include "absl/log/absl_check.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" -#include "common/casting.h" #include "common/value.h" #include "runtime/activation.h" #include "google/protobuf/arena.h" @@ -49,6 +49,43 @@ absl::Status BindProtoToActivation( google::protobuf::MessageFactory* absl_nonnull message_factory, google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation); +template +absl::Status BindProtoToActivationImpl( + const T& context, BindProtoUnsetFieldBehavior unset_field_behavior, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation) { + static_assert(std::is_base_of_v); + + Value parent; + if constexpr (kBorrow) { + parent = Value::WrapMessageUnsafe(&context, descriptor_pool, + message_factory, arena); + } else { + parent = + Value::FromMessage(context, descriptor_pool, message_factory, arena); + } + + if (!parent.IsStruct()) { + return absl::InvalidArgumentError( + absl::StrCat("context is a well-known type: ", context.GetTypeName())); + } + StructValue struct_value = parent.GetStruct(); + + const google::protobuf::Descriptor* descriptor = context.GetDescriptor(); + ABSL_DCHECK(descriptor != nullptr); + if (descriptor == nullptr) { + // Generally not possible, but don't crash in case of a misbehaving + // implementation in normal builds. + return absl::InvalidArgumentError( + absl::StrCat("context missing descriptor: ", context.GetTypeName())); + } + + return BindProtoToActivation(*descriptor, struct_value, unset_field_behavior, + descriptor_pool, message_factory, arena, + activation); +} + } // namespace runtime_internal // Utility method, that takes a protobuf Message and interprets it as a @@ -89,26 +126,9 @@ absl::Status BindProtoToActivation( const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, google::protobuf::MessageFactory* absl_nonnull message_factory, google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation) { - static_assert(std::is_base_of_v); - Value parent = - Value::FromMessage(context, descriptor_pool, message_factory, arena); - - if (!InstanceOf(parent)) { - return absl::InvalidArgumentError( - absl::StrCat("context is a well-known type: ", context.GetTypeName())); - } - const StructValue& struct_value = Cast(parent); - - const google::protobuf::Descriptor* descriptor = context.GetDescriptor(); - - if (descriptor == nullptr) { - return absl::InvalidArgumentError( - absl::StrCat("context missing descriptor: ", context.GetTypeName())); - } - - return runtime_internal::BindProtoToActivation( - *descriptor, struct_value, unset_field_behavior, descriptor_pool, - message_factory, arena, activation); + return runtime_internal::BindProtoToActivationImpl( + context, unset_field_behavior, descriptor_pool, message_factory, arena, + activation); } template @@ -122,6 +142,33 @@ absl::Status BindProtoToActivation( activation); } +// Like `BindProtoToActivation`, but uses `Value::WrapMessageUnsafe` to borrow +// from `context` rather than copying fields to `arena`. +// +// Requires the caller to keep the context message valid as long as the +// activation or any derived value. +template +absl::Status BindProtoViewToActivation( + const T& context, BindProtoUnsetFieldBehavior unset_field_behavior, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation) { + return runtime_internal::BindProtoToActivationImpl( + context, unset_field_behavior, descriptor_pool, message_factory, arena, + activation); +} + +template +absl::Status BindProtoViewToActivation( + const T& context, + const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool, + google::protobuf::MessageFactory* absl_nonnull message_factory, + google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation) { + return BindProtoViewToActivation(context, BindProtoUnsetFieldBehavior::kSkip, + descriptor_pool, message_factory, arena, + activation); +} + } // namespace cel #endif // THIRD_PARTY_CEL_CPP_RUNTIME_BIND_PROTO_TO_ACTIVATION_H_ diff --git a/runtime/bind_proto_to_activation_test.cc b/runtime/bind_proto_to_activation_test.cc index 2242f47c0..e7935b92a 100644 --- a/runtime/bind_proto_to_activation_test.cc +++ b/runtime/bind_proto_to_activation_test.cc @@ -240,5 +240,39 @@ TEST_F(BindProtoToActivationTest, BindProtoToActivationMapComplex) { IsOkAndHolds(Optional(IsMapValueOfSize(2)))); } +TEST_F(BindProtoToActivationTest, BindProtoViewToActivation) { + TestAllTypes test_all_types; + test_all_types.set_single_int64(123); + Activation activation; + + ASSERT_THAT( + BindProtoViewToActivation(test_all_types, descriptor_pool(), + message_factory(), arena(), &activation), + IsOk()); + + EXPECT_THAT(activation.FindVariable("single_int64", descriptor_pool(), + message_factory(), arena()), + IsOkAndHolds(Optional(IntValueIs(123)))); +} + +TEST_F(BindProtoToActivationTest, BindProtoViewToActivationDefault) { + TestAllTypes test_all_types; + test_all_types.set_single_int64(123); + Activation activation; + + ASSERT_THAT( + BindProtoViewToActivation( + test_all_types, BindProtoUnsetFieldBehavior::kBindDefaultValue, + descriptor_pool(), message_factory(), arena(), &activation), + IsOk()); + + EXPECT_THAT(activation.FindVariable("single_int32", descriptor_pool(), + message_factory(), arena()), + IsOkAndHolds(Optional(IntValueIs(-32)))); + EXPECT_THAT(activation.FindVariable("single_sint32", descriptor_pool(), + message_factory(), arena()), + IsOkAndHolds(Optional(IntValueIs(0)))); +} + } // namespace } // namespace cel