diff --git a/BUILD b/BUILD index 79844a80f5..f967bd9414 100644 --- a/BUILD +++ b/BUILD @@ -282,6 +282,26 @@ cc_library( ], ) +cc_library( + name = "message_copy", + srcs = [ + "upb/message/copy.c", + ], + hdrs = [ + "upb/message/copy.h", + ], + copts = UPB_DEFAULT_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":collections_internal", + ":message_accessors", + ":message_internal", + ":mini_table_internal", + ":port", + ":upb", + ], +) + cc_test( name = "mini_table_encode_test", srcs = [ @@ -317,6 +337,24 @@ cc_test( ], ) +cc_test( + name = "message_copy_test", + srcs = ["upb/message/copy_test.cc"], + deps = [ + ":collections", + ":message_accessors", + ":message_copy", + ":mini_table_internal", + ":upb", + "//upb/test:test_messages_proto2_upb_proto", + "//upb/test:test_messages_proto3_upb_proto", + "//upb/test:test_upb_proto", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_googletest//:gtest_main", + "@com_google_protobuf//:protobuf", + ], +) + cc_library( name = "fastdecode", copts = UPB_DEFAULT_COPTS, diff --git a/protos/protos.h b/protos/protos.h index e3dd968c2a..d3ec430cc5 100644 --- a/protos/protos.h +++ b/protos/protos.h @@ -116,6 +116,12 @@ typename T::Proxy CreateMessage(::protos::Arena& arena) { arena.ptr()); } +template +typename T::Proxy CloneMessage(Ptr message, upb::Arena& arena) { + return typename T::Proxy( + upb_Message_DeepClone(message, T::minitable(), arena.ptr()), arena.ptr()); +} + // begin:github_only // This type exists to work around an absl type that has not yet been // released. diff --git a/upb/message/copy.c b/upb/message/copy.c new file mode 100644 index 0000000000..a8c331567d --- /dev/null +++ b/upb/message/copy.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "upb/message/copy.h" + +#include "upb/mem/arena.h" +#include "upb/message/accessors.h" +#include "upb/message/message.h" + +// Must be last. +#include "upb/mini_table/common.h" +#include "upb/port/def.inc" + +static bool upb_MessageField_IsMap(const upb_MiniTableField* field) { + return upb_FieldMode_Get(field) == kUpb_FieldMode_Map; +} + +static upb_StringView upb_Clone_StringView(upb_StringView str, + upb_Arena* arena) { + if (str.size == 0) { + return upb_StringView_FromDataAndSize(NULL, 0); + } + void* cloned_data = upb_Arena_Malloc(arena, str.size); + upb_StringView cloned_str = + upb_StringView_FromDataAndSize(cloned_data, str.size); + memcpy(cloned_data, str.data, str.size); + return cloned_str; +} + +static bool upb_Clone_MessageValue(void* value, upb_CType value_type, + const upb_MiniTable* sub, upb_Arena* arena) { + switch (value_type) { + case kUpb_CType_Bool: + case kUpb_CType_Float: + case kUpb_CType_Int32: + case kUpb_CType_UInt32: + case kUpb_CType_Enum: + case kUpb_CType_Double: + case kUpb_CType_Int64: + case kUpb_CType_UInt64: + return true; + case kUpb_CType_String: + case kUpb_CType_Bytes: { + upb_StringView source = *(upb_StringView*)value; + int size = source.size; + void* cloned_data = upb_Arena_Malloc(arena, size); + if (cloned_data == NULL) { + return false; + } + *(upb_StringView*)value = + upb_StringView_FromDataAndSize(cloned_data, size); + memcpy(cloned_data, source.data, size); + return true; + } break; + case kUpb_CType_Message: { + UPB_ASSERT(sub); + const upb_Message* source = *(upb_Message**)value; + UPB_ASSERT(source); + upb_Message* clone = upb_Message_DeepClone(source, sub, arena); + *(upb_Message**)value = clone; + return clone != NULL; + } break; + } + UPB_UNREACHABLE(); +} + +upb_Map* upb_Map_DeepClone(const upb_Map* map, upb_CType key_type, + upb_CType value_type, + const upb_MiniTable* map_entry_table, + upb_Arena* arena) { + upb_Map* cloned_map = _upb_Map_New(arena, map->key_size, map->val_size); + if (cloned_map == NULL) { + return NULL; + } + upb_MessageValue key, val; + size_t iter = kUpb_Map_Begin; + while (upb_Map_Next(map, &key, &val, &iter)) { + const upb_MiniTableField* value_field = &map_entry_table->fields[1]; + const upb_MiniTable* value_sub = + (value_field->submsg_index != kUpb_NoSub) + ? upb_MiniTable_GetSubMessageTable(map_entry_table, value_field) + : NULL; + upb_CType value_field_type = upb_MiniTableField_CType(value_field); + if (!upb_Clone_MessageValue(&val, value_field_type, value_sub, arena)) { + return NULL; + } + if (upb_Map_Insert(cloned_map, key, val, arena) == + kUpb_MapInsertStatus_OutOfMemory) { + return NULL; + } + } + return cloned_map; +} + +static upb_Map* upb_Message_Map_DeepClone(const upb_Map* map, + const upb_MiniTable* mini_table, + const upb_MiniTableField* field, + upb_Message* clone, + upb_Arena* arena) { + const upb_MiniTable* map_entry_table = + mini_table->subs[field->submsg_index].submsg; + UPB_ASSERT(map_entry_table); + + const upb_MiniTableField* key_field = &map_entry_table->fields[0]; + const upb_MiniTableField* value_field = &map_entry_table->fields[1]; + + upb_Map* cloned_map = upb_Map_DeepClone( + map, upb_MiniTableField_CType(key_field), + upb_MiniTableField_CType(value_field), map_entry_table, arena); + if (!cloned_map) { + return NULL; + } + _upb_Message_SetNonExtensionField(clone, field, &cloned_map); + return cloned_map; +} + +upb_Array* upb_Array_DeepClone(const upb_Array* array, upb_CType value_type, + const upb_MiniTable* sub, upb_Arena* arena) { + size_t size = array->size; + upb_Array* cloned_array = + _upb_Array_New(arena, size, _upb_Array_CTypeSizeLg2(value_type)); + if (!cloned_array) { + return NULL; + } + for (int i = 0; i < size; ++i) { + upb_MessageValue val = upb_Array_Get(array, i); + if (!upb_Clone_MessageValue(&val, value_type, sub, arena)) { + return false; + } + upb_Array_Set(cloned_array, i, val); + } + return cloned_array; +} + +static bool upb_Message_Array_DeepClone(const upb_Array* array, + const upb_MiniTable* mini_table, + const upb_MiniTableField* field, + upb_Message* clone, upb_Arena* arena) { + _upb_MiniTableField_CheckIsArray(field); + upb_Array* cloned_array = upb_Array_DeepClone( + array, upb_MiniTableField_CType(field), + field->submsg_index != kUpb_NoSub + ? upb_MiniTable_GetSubMessageTable(mini_table, field) + : NULL, + arena); + + // Clear out upb_Array* due to parent memcpy. + _upb_Message_SetNonExtensionField(clone, field, &cloned_array); + return true; +} + +static bool upb_Clone_ExtensionValue( + const upb_MiniTableExtension* mini_table_ext, + const upb_Message_Extension* source, upb_Message_Extension* dest, + upb_Arena* arena) { + dest->data = source->data; + return upb_Clone_MessageValue( + &dest->data, upb_MiniTableField_CType(&mini_table_ext->field), + mini_table_ext->sub.submsg, arena); +} + +// Deep clones a message using the provided target arena. +// +// Returns NULL on failure. +upb_Message* upb_Message_DeepClone(const upb_Message* message, + const upb_MiniTable* mini_table, + upb_Arena* arena) { + upb_Message* clone = upb_Message_New(mini_table, arena); + upb_StringView empty_string = upb_StringView_FromDataAndSize(NULL, 0); + // Only copy message area skipping upb_Message_Internal. + memcpy(clone, message, mini_table->size); + for (size_t i = 0; i < mini_table->field_count; ++i) { + const upb_MiniTableField* field = &mini_table->fields[i]; + if (!upb_IsRepeatedOrMap(field)) { + switch (field->descriptortype) { + case kUpb_FieldType_Group: + case kUpb_FieldType_Message: { + const upb_Message* sub_message = + upb_Message_GetMessage(message, field, NULL); + if (sub_message != NULL) { + const upb_MiniTable* sub_message_table = + upb_MiniTable_GetSubMessageTable(mini_table, field); + upb_Message* cloned_sub_message = + upb_Message_DeepClone(sub_message, sub_message_table, arena); + if (cloned_sub_message == NULL) { + return NULL; + } + upb_Message_SetMessage(clone, mini_table, field, + cloned_sub_message); + } + } break; + case kUpb_FieldType_String: + case kUpb_FieldType_Bytes: { + upb_StringView str = + upb_Message_GetString(message, field, empty_string); + if (str.size != 0) { + if (!upb_Message_SetString( + clone, field, upb_Clone_StringView(str, arena), arena)) { + return NULL; + } + } + } break; + default: + // Scalar, already copied. + break; + } + } else { + if (upb_MessageField_IsMap(field)) { + const upb_Map* map = upb_Message_GetMap(message, field); + if (map != NULL) { + if (!upb_Message_Map_DeepClone(map, mini_table, field, clone, + arena)) { + return NULL; + } + } + } else { + const upb_Array* array = upb_Message_GetArray(message, field); + if (array != NULL) { + if (!upb_Message_Array_DeepClone(array, mini_table, field, clone, + arena)) { + return NULL; + } + } + } + } + } + // Clone extensions. + size_t ext_count; + const upb_Message_Extension* ext = _upb_Message_Getexts(message, &ext_count); + for (size_t i = 0; i < ext_count; ++i) { + const upb_Message_Extension* msg_ext = &ext[i]; + upb_Message_Extension* cloned_ext = + _upb_Message_GetOrCreateExtension(clone, msg_ext->ext, arena); + if (!cloned_ext) { + return NULL; + } + if (!upb_Clone_ExtensionValue(msg_ext->ext, msg_ext, cloned_ext, arena)) { + return NULL; + } + } + + // Clone unknowns. + size_t unknown_size = 0; + const char* ptr = upb_Message_GetUnknown(message, &unknown_size); + if (unknown_size != 0) { + UPB_ASSERT(ptr); + // Make a copy into destination arena. + void* cloned_unknowns = upb_Arena_Malloc(arena, unknown_size); + if (cloned_unknowns == NULL) { + return NULL; + } + memcpy(cloned_unknowns, ptr, unknown_size); + if (!_upb_Message_AddUnknown(clone, cloned_unknowns, unknown_size, arena)) { + return NULL; + } + } + return clone; +} diff --git a/upb/message/copy.h b/upb/message/copy.h new file mode 100644 index 0000000000..18508a183a --- /dev/null +++ b/upb/message/copy.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2009-2022, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_MESSAGE_COPY_H_ +#define UPB_MESSAGE_COPY_H_ + +#include "upb/collections/message_value.h" +#include "upb/message/internal.h" +#include "upb/mini_table/common.h" + +// Must be last. +#include "upb/port/def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +// Deep clones a message using the provided target arena. +upb_Message* upb_Message_DeepClone(const upb_Message* message, + const upb_MiniTable* mini_table, + upb_Arena* arena); + +// Deep clones array contents. +upb_Array* upb_Array_DeepClone(const upb_Array* array, upb_CType value_type, + const upb_MiniTable* sub, upb_Arena* arena); + +// Deep clones map contents. +upb_Map* upb_Map_DeepClone(const upb_Map* map, upb_CType key_type, + upb_CType value_type, + const upb_MiniTable* map_entry_table, + upb_Arena* arena); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port/undef.inc" + +#endif // UPB_MESSAGE_COPY_H_ diff --git a/upb/message/copy_test.cc b/upb/message/copy_test.cc new file mode 100644 index 0000000000..304cfa4609 --- /dev/null +++ b/upb/message/copy_test.cc @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Test of mini table accessors. + * + * Messages are created and mutated using generated code, and then + * accessed through reflective APIs exposed through mini table accessors. + */ + +#include "upb/message/copy.h" + +#include "gtest/gtest.h" +#include "google/protobuf/test_messages_proto2.upb.h" +#include "google/protobuf/test_messages_proto3.upb.h" +#include "upb/base/string_view.h" +#include "upb/collections/array.h" +#include "upb/collections/map.h" +#include "upb/mem/arena.h" +#include "upb/message/accessors.h" +#include "upb/mini_table/common.h" +#include "upb/mini_table/encode_internal.hpp" +#include "upb/mini_table/field_internal.h" +#include "upb/test/test.upb.h" +#include "upb/upb.h" + +namespace { + +// Proto2 test messages field numbers used for reflective access. +const uint32_t kFieldOptionalInt32 = 1; +const uint32_t kFieldOptionalString = 14; +const uint32_t kFieldOptionalNestedMessage = 18; + +const char kTestStr1[] = "Hello1"; +const char kTestStr2[] = "HelloWorld2"; +const int32_t kTestInt32 = 567; +const int32_t kTestNestedInt32 = 123; + +const upb_MiniTableField* find_proto2_field(int field_number) { + return upb_MiniTable_FindFieldByNumber( + &protobuf_test_messages_proto2_TestAllTypesProto2_msg_init, field_number); +} + +TEST(GeneratedCode, DeepCloneMessageScalarAndString) { + upb_Arena* source_arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2* msg = + protobuf_test_messages_proto2_TestAllTypesProto2_new(source_arena); + const upb_MiniTableField* optional_int32_field = + find_proto2_field(kFieldOptionalInt32); + const upb_MiniTableField* optional_string_field = + find_proto2_field(kFieldOptionalString); + upb_Message_SetInt32(msg, optional_int32_field, kTestInt32, nullptr); + char* string_in_arena = + (char*)upb_Arena_Malloc(source_arena, sizeof(kTestStr1)); + memcpy(string_in_arena, kTestStr1, sizeof(kTestStr1)); + upb_Message_SetString( + msg, optional_string_field, + upb_StringView_FromDataAndSize(string_in_arena, sizeof(kTestStr1) - 1), + source_arena); + upb_Arena* arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2* clone = + (protobuf_test_messages_proto2_TestAllTypesProto2*)upb_Message_DeepClone( + msg, &protobuf_test_messages_proto2_TestAllTypesProto2_msg_init, + arena); + // After cloning overwrite values and destroy source arena for MSAN. + memset(string_in_arena, 0, sizeof(kTestStr1)); + upb_Arena_Free(source_arena); + EXPECT_TRUE(upb_Message_HasField(clone, optional_int32_field)); + EXPECT_EQ(upb_Message_GetInt32(clone, optional_int32_field, 0), kTestInt32); + EXPECT_TRUE(upb_Message_HasField(clone, optional_string_field)); + EXPECT_EQ(upb_Message_GetString(clone, optional_string_field, + upb_StringView_FromDataAndSize(nullptr, 0)) + .size, + sizeof(kTestStr1) - 1); + EXPECT_TRUE(upb_StringView_IsEqual( + upb_Message_GetString(clone, optional_string_field, + upb_StringView_FromDataAndSize(nullptr, 0)), + upb_StringView_FromString(kTestStr1))); + upb_Arena_Free(arena); +} + +TEST(GeneratedCode, DeepCloneMessageSubMessage) { + upb_Arena* source_arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2* msg = + protobuf_test_messages_proto2_TestAllTypesProto2_new(source_arena); + const upb_MiniTableField* nested_message_field = + find_proto2_field(kFieldOptionalNestedMessage); + protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage* nested = + protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage_new( + source_arena); + protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage_set_a( + nested, kTestNestedInt32); + upb_Message_SetMessage( + msg, &protobuf_test_messages_proto2_TestAllTypesProto2_msg_init, + nested_message_field, nested); + upb_Arena* arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2* clone = + (protobuf_test_messages_proto2_TestAllTypesProto2*)upb_Message_DeepClone( + msg, &protobuf_test_messages_proto2_TestAllTypesProto2_msg_init, + arena); + // After cloning overwrite values and destroy source arena for MSAN. + protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage_set_a(nested, + 0); + upb_Arena_Free(source_arena); + EXPECT_TRUE(upb_Message_HasField(clone, nested_message_field)); + protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage* + cloned_nested = + (protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage*) + upb_Message_GetMessage(clone, nested_message_field, nullptr); + EXPECT_EQ(protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage_a( + cloned_nested), + kTestNestedInt32); + upb_Arena_Free(arena); +} + +TEST(GeneratedCode, DeepCloneMessageMapField) { + upb_Arena* source_arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2* msg = + protobuf_test_messages_proto2_TestAllTypesProto2_new(source_arena); + ASSERT_TRUE( + protobuf_test_messages_proto2_TestAllTypesProto2_map_int32_double_set( + msg, 12, 1200.5, source_arena)); + ASSERT_TRUE( + protobuf_test_messages_proto2_TestAllTypesProto2_map_string_string_set( + msg, upb_StringView_FromString("key1"), + upb_StringView_FromString("value1"), source_arena)); + protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage* nested = + protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage_new( + source_arena); + protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage_set_a( + nested, kTestNestedInt32); + ASSERT_TRUE( + protobuf_test_messages_proto2_TestAllTypesProto2_map_string_nested_message_set( + msg, upb_StringView_FromString("nestedkey1"), nested, source_arena)); + + upb_Arena* arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2* clone = + (protobuf_test_messages_proto2_TestAllTypesProto2*)upb_Message_DeepClone( + msg, &protobuf_test_messages_proto2_TestAllTypesProto2_msg_init, + arena); + protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage_set_a(nested, + 0); + upb_Arena_Free(source_arena); + size_t iter = kUpb_Map_Begin; + // Test map. + const protobuf_test_messages_proto2_TestAllTypesProto2_MapInt32DoubleEntry* + int32_double_entry = + protobuf_test_messages_proto2_TestAllTypesProto2_map_int32_double_next( + clone, &iter); + ASSERT_NE(int32_double_entry, nullptr); + EXPECT_EQ( + protobuf_test_messages_proto2_TestAllTypesProto2_MapInt32DoubleEntry_key( + int32_double_entry), + 12); + EXPECT_EQ( + protobuf_test_messages_proto2_TestAllTypesProto2_MapInt32DoubleEntry_value( + int32_double_entry), + 1200.5); + // Test map. + iter = kUpb_Map_Begin; + const protobuf_test_messages_proto2_TestAllTypesProto2_MapStringStringEntry* + string_string_entry = + protobuf_test_messages_proto2_TestAllTypesProto2_map_string_string_next( + clone, &iter); + ASSERT_NE(string_string_entry, nullptr); + EXPECT_TRUE(upb_StringView_IsEqual( + protobuf_test_messages_proto2_TestAllTypesProto2_MapStringStringEntry_key( + string_string_entry), + upb_StringView_FromString("key1"))); + EXPECT_TRUE(upb_StringView_IsEqual( + protobuf_test_messages_proto2_TestAllTypesProto2_MapStringStringEntry_value( + string_string_entry), + upb_StringView_FromString("value1"))); + // Test map. + iter = kUpb_Map_Begin; + const protobuf_test_messages_proto2_TestAllTypesProto2_MapStringNestedMessageEntry* + nested_message_entry = + protobuf_test_messages_proto2_TestAllTypesProto2_map_string_nested_message_next( + clone, &iter); + ASSERT_NE(nested_message_entry, nullptr); + EXPECT_TRUE(upb_StringView_IsEqual( + protobuf_test_messages_proto2_TestAllTypesProto2_MapStringNestedMessageEntry_key( + nested_message_entry), + upb_StringView_FromString("nestedkey1"))); + const protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage* + cloned_nested = + protobuf_test_messages_proto2_TestAllTypesProto2_MapStringNestedMessageEntry_value( + nested_message_entry); + ASSERT_NE(cloned_nested, nullptr); + EXPECT_EQ(protobuf_test_messages_proto2_TestAllTypesProto2_NestedMessage_a( + cloned_nested), + kTestNestedInt32); + upb_Arena_Free(arena); +} + +TEST(GeneratedCode, DeepCloneMessageExtensions) { + // Alloc and fill in test message with extension. + upb_Arena* source_arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrect* msg = + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrect_new( + source_arena); + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrectExtension1* + ext1 = + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrectExtension1_new( + source_arena); + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrectExtension1_set_str( + ext1, upb_StringView_FromString(kTestStr1)); + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrectExtension1_set_message_set_extension( + msg, ext1, source_arena); + // Create clone. + upb_Arena* arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrect* clone = + (protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrect*) + upb_Message_DeepClone( + msg, + &protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrect_msg_init, + arena); + + // Mutate original extension. + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrectExtension1_set_str( + ext1, upb_StringView_FromString(kTestStr2)); + upb_Arena_Free(source_arena); + + const protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrectExtension1* + cloned_ext = + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrectExtension1_message_set_extension( + clone); + ASSERT_NE(cloned_ext, nullptr); + EXPECT_TRUE(upb_StringView_IsEqual( + protobuf_test_messages_proto2_TestAllTypesProto2_MessageSetCorrectExtension1_str( + cloned_ext), + upb_StringView_FromString(kTestStr1))); + upb_Arena_Free(arena); +} +} // namespace