From 2dc0ec30027145e936dd1d0432ba9677e1d3a97b Mon Sep 17 00:00:00 2001 From: Justin King Date: Thu, 24 Sep 2026 13:53:57 -0700 Subject: [PATCH] Add utility to clone `CelValue` onto the same or different `google::protobuf::Arena` PiperOrigin-RevId: 987735736 --- eval/public/BUILD | 22 +++++ eval/public/cel_value_clone.cc | 166 +++++++++++++++++++++++++++++++++ eval/public/cel_value_clone.h | 40 ++++++++ 3 files changed, 228 insertions(+) create mode 100644 eval/public/cel_value_clone.cc create mode 100644 eval/public/cel_value_clone.h diff --git a/eval/public/BUILD b/eval/public/BUILD index 0c9c6f23e..a0a3bcb27 100644 --- a/eval/public/BUILD +++ b/eval/public/BUILD @@ -114,6 +114,28 @@ cc_library( ], ) +cc_library( + name = "cel_value_clone", + srcs = ["cel_value_clone.cc"], + hdrs = ["cel_value_clone.h"], + deps = [ + ":cel_value", + ":message_wrapper", + ":unknown_set", + "//eval/public/containers:container_backed_list_impl", + "//eval/public/containers:container_backed_map_impl", + "//internal:status_macros", + "@com_google_absl//absl/log:absl_check", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:cord", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:optional", + "@com_google_protobuf//:protobuf", + ], +) + cc_library( name = "cel_attribute", srcs = [ diff --git a/eval/public/cel_value_clone.cc b/eval/public/cel_value_clone.cc new file mode 100644 index 000000000..8a07033df --- /dev/null +++ b/eval/public/cel_value_clone.cc @@ -0,0 +1,166 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "eval/public/cel_value_clone.h" + +#include +#include +#include +#include + +#include "absl/log/absl_check.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/cord.h" +#include "absl/strings/str_cat.h" +#include "absl/time/time.h" +#include "absl/types/optional.h" +#include "eval/public/cel_value.h" +#include "eval/public/containers/container_backed_list_impl.h" +#include "eval/public/containers/container_backed_map_impl.h" +#include "eval/public/message_wrapper.h" +#include "eval/public/unknown_set.h" +#include "internal/status_macros.h" +#include "google/protobuf/arena.h" + +namespace google::api::expr::runtime { + +namespace { + +struct CloneVisitor { + google::protobuf::Arena* const arena; + + absl::StatusOr operator()(bool arg) { + return CelValue::CreateBool(arg); + } + absl::StatusOr operator()(int64_t arg) { + return CelValue::CreateInt64(arg); + } + absl::StatusOr operator()(uint64_t arg) { + return CelValue::CreateUint64(arg); + } + absl::StatusOr operator()(double arg) { + return CelValue::CreateDouble(arg); + } + absl::StatusOr operator()(CelValue::NullType) { + return CelValue::CreateNull(); + } + + absl::StatusOr operator()(CelValue::StringHolder arg) { + if (arg.value().empty()) { + return CelValue::CreateStringView(""); + } + return CelValue::CreateString(CelValue::StringHolder( + google::protobuf::Arena::Create(arena, arg.value()))); + } + + absl::StatusOr operator()(CelValue::BytesHolder arg) { + if (arg.value().empty()) { + return CelValue::CreateBytesView(""); + } + return CelValue::CreateBytes(CelValue::BytesHolder( + google::protobuf::Arena::Create(arena, arg.value()))); + } + + absl::StatusOr operator()(const MessageWrapper& arg) { + if (arg.HasFullProto()) { + google::protobuf::Message* dup = + static_cast(arg.message_ptr())->New(arena); + dup->CopyFrom(*static_cast(arg.message_ptr())); + return CelValue::CreateMessageWrapper( + MessageWrapper(dup, arg.legacy_type_info())); + } + google::protobuf::MessageLite* dup = arg.message_ptr()->New(arena); + absl::Cord serialized; + if (!arg.message_ptr()->SerializePartialToCord(&serialized)) { + return absl::UnknownError( + absl::StrCat("failed to serialize message: ", dup->GetTypeName())); + } + if (!dup->ParsePartialFromString(serialized)) { + return absl::UnknownError( + absl::StrCat("failed to parse message: ", dup->GetTypeName())); + } + return CelValue::CreateMessageWrapper( + MessageWrapper(dup, arg.legacy_type_info())); + } + + absl::StatusOr operator()(absl::Duration arg) { + return CelValue::CreateUncheckedDuration(arg); + } + + absl::StatusOr operator()(absl::Time arg) { + return CelValue::CreateTimestamp(arg); + } + + absl::StatusOr operator()(const CelList* arg) { + if (arg->empty()) { + return CelValue::CreateList(); + } + std::vector elements; + elements.reserve(arg->size()); + for (int i = 0; i < arg->size(); i++) { + CEL_ASSIGN_OR_RETURN(auto element, + CelValueClone(arena, arg->Get(arena, i))); + elements.push_back(element); + } + return CelValue::CreateList(google::protobuf::Arena::Create( + arena, std::move(elements))); + } + + absl::StatusOr operator()(const CelMap* arg) { + if (arg->empty()) { + return CelValue::CreateMap(); + } + CEL_ASSIGN_OR_RETURN(auto keys, arg->ListKeys(arena)); + CelMapBuilder* builder = google::protobuf::Arena::Create(arena); + for (int i = 0; i < keys->size(); ++i) { + CelValue key = keys->Get(arena, i); + absl::optional value = arg->Get(arena, key); + if (!value.has_value()) { + return absl::UnknownError( + "CelMap::ListKeys returned key but CelMap::Get did not find the " + "entry"); + } + CEL_ASSIGN_OR_RETURN(auto key_dup, CelValueClone(arena, key)); + CEL_ASSIGN_OR_RETURN(auto value_dup, CelValueClone(arena, *value)); + CEL_RETURN_IF_ERROR(builder->Add(key_dup, value_dup)); + } + return CelValue::CreateMap(builder); + } + + absl::StatusOr operator()(const UnknownSet* arg) { + return CelValue::CreateUnknownSet( + google::protobuf::Arena::Create(arena, *arg)); + } + + absl::StatusOr operator()(CelValue::CelTypeHolder arg) { + return CelValue::CreateCelType(CelValue::CelTypeHolder( + google::protobuf::Arena::Create(arena, arg.value()))); + } + + absl::StatusOr operator()(const CelError* arg) { + return CelValue::CreateError(google::protobuf::Arena::Create(arena, *arg)); + } +}; + +} // namespace + +absl::StatusOr CelValueClone(google::protobuf::Arena* arena, CelValue in) { + ABSL_DCHECK(arena != nullptr); + return in.Visit>(CloneVisitor{.arena = arena}); +} + +} // namespace google::api::expr::runtime diff --git a/eval/public/cel_value_clone.h b/eval/public/cel_value_clone.h new file mode 100644 index 000000000..2ffe47894 --- /dev/null +++ b/eval/public/cel_value_clone.h @@ -0,0 +1,40 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef THIRD_PARTY_CEL_CPP_EVAL_PUBLIC_CEL_VALUE_CLONE_H_ +#define THIRD_PARTY_CEL_CPP_EVAL_PUBLIC_CEL_VALUE_CLONE_H_ + +#include "absl/status/statusor.h" +#include "eval/public/cel_value.h" +#include "google/protobuf/arena.h" + +namespace google::api::expr::runtime { + +// Clones (deep copy) a CelValue such that the resulting CelValue has a lifetime +// tied to the given arena. The container implementations for the output are not +// guaranteed to be the same as the input. +absl::StatusOr CelValueClone(google::protobuf::Arena* arena, CelValue in); +inline absl::StatusOr CelValueClone(google::protobuf::Arena* arena, + absl::StatusOr in) { + if (!in.ok()) { + return in; + } + return CelValueClone(arena, *in); +} + +} // namespace google::api::expr::runtime + +#endif // THIRD_PARTY_CEL_CPP_EVAL_PUBLIC_CEL_VALUE_CLONE_H_