Reorganize upb file structure This change moves almost everything in the `upb/` directory up one level, so that for example `upb/upb/generated_code_support.h` becomes just `upb/generated_code_support.h`. The only exceptions I made to this were that I left `upb/cmake` and `upb/BUILD` where they are, mostly because that avoids conflict with other files and the current locations seem reasonable for now. The `python/` directory is a little bit of a challenge because we had to merge the existing directory there with `upb/python/`. I made `upb/python/BUILD` into the BUILD file for the merged directory, and it effectively loads the contents of the other BUILD file via `python/build_targets.bzl`, but I plan to clean this up soon. PiperOrigin-RevId: 568651768
diff --git a/protos_generator/BUILD b/protos_generator/BUILD new file mode 100644 index 0000000..fae587a --- /dev/null +++ b/protos_generator/BUILD
@@ -0,0 +1,101 @@ +# Copyright (c) 2009-2021, Google LLC +# All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +load( + "//bazel:build_defs.bzl", + "UPB_DEFAULT_CPPOPTS", +) + +# begin:google_only +# package(default_applicable_licenses = ["//upb:license"]) +# end:google_only + +licenses(["notice"]) + +cc_binary( + name = "protoc-gen-upb-protos", + srcs = [ + "protoc-gen-upb-protos.cc", + ], + copts = UPB_DEFAULT_CPPOPTS, + visibility = ["//visibility:public"], + deps = [ + ":gen_utils", + ":generator", + ":names", + ":output", + "//:protobuf", + "//src/google/protobuf/compiler:code_generator", + "//upbc:file_layout", + ], +) + +cc_library( + name = "generator", + srcs = [ + "gen_accessors.cc", + "gen_enums.cc", + "gen_extensions.cc", + "gen_messages.cc", + "gen_repeated_fields.cc", + ], + hdrs = [ + "gen_accessors.h", + "gen_enums.h", + "gen_extensions.h", + "gen_messages.h", + "gen_repeated_fields.h", + ], + visibility = ["//visibility:private"], + deps = [ + ":gen_utils", + ":names", + ":output", + "//:protobuf", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/strings", + "//upbc:common", + "//upbc:file_layout", + "//upbc:keywords", + "//upbc:names", + ], +) + +cc_library( + name = "output", + srcs = ["output.cc"], + hdrs = ["output.h"], + visibility = ["//visibility:private"], + deps = [ + "//:protobuf", + "@com_google_absl//absl/log:absl_log", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "gen_utils", + srcs = ["gen_utils.cc"], + hdrs = ["gen_utils.h"], + visibility = ["//visibility:public"], + deps = [ + "//:protobuf", + "//src/google/protobuf/compiler:code_generator", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "names", + srcs = ["names.cc"], + hdrs = ["names.h"], + visibility = ["//visibility:private"], + deps = [ + ":output", + "//upbc:keywords", + ], +)
diff --git a/protos_generator/README.md b/protos_generator/README.md new file mode 100644 index 0000000..05b64c0 --- /dev/null +++ b/protos_generator/README.md
@@ -0,0 +1,8 @@ +`protos` Generator +================== + +This directory contains the generator for the [`protos` +API](https://github.com/protocolbuffers/protobuf/tree/main/protos), an +experimental C++ protobuf implementation. Most users should use the standard +C++ implementation +[here](https://github.com/protocolbuffers/protobuf/tree/main/src).
diff --git a/protos_generator/gen_accessors.cc b/protos_generator/gen_accessors.cc new file mode 100644 index 0000000..410a42b --- /dev/null +++ b/protos_generator/gen_accessors.cc
@@ -0,0 +1,592 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 "protos_generator/gen_accessors.h" + +#include <string> + +#include "absl/container/flat_hash_set.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "google/protobuf/descriptor.h" +#include "protos_generator/gen_repeated_fields.h" +#include "protos_generator/gen_utils.h" +#include "protos_generator/names.h" +#include "protos_generator/output.h" +#include "upbc/common.h" +#include "upbc/keywords.h" +#include "upbc/names.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +using NameToFieldDescriptorMap = + absl::flat_hash_map<absl::string_view, const protobuf::FieldDescriptor*>; + +void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + absl::string_view resolved_field_name, + absl::string_view resolved_upbc_name, + Output& output); +void WriteFieldAccessorClear(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + absl::string_view resolved_field_name, + absl::string_view resolved_upbc_name, + Output& output); +void WriteMapFieldAccessors(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + absl::string_view resolved_field_name, + absl::string_view resolved_upbc_name, + Output& output); + +void WriteMapAccessorDefinitions(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + absl::string_view resolved_field_name, + absl::string_view class_name, Output& output); + +// Returns C++ class member name by resolving naming conflicts across +// proto field names (such as clear_ prefixes) and keyword collisions. +// +// The Upb C generator prefixes all accessors with package and class names +// avoiding collisions. Therefore we need to use raw field names when calling +// into C accessors but need to fully resolve conflicts for C++ class members. +std::string ResolveFieldName(const protobuf::FieldDescriptor* field, + const NameToFieldDescriptorMap& field_names); + +NameToFieldDescriptorMap CreateFieldNameMap( + const protobuf::Descriptor* message) { + NameToFieldDescriptorMap field_names; + for (int i = 0; i < message->field_count(); i++) { + const protobuf::FieldDescriptor* field = message->field(i); + field_names.emplace(field->name(), field); + } + return field_names; +} + +void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc, + Output& output) { + // Generate const methods. + OutputIndenter i(output); + + auto field_names = CreateFieldNameMap(desc); + auto upbc_field_names = upbc::CreateFieldNameMap(desc); + + for (const auto* field : FieldNumberOrder(desc)) { + std::string resolved_field_name = ResolveFieldName(field, field_names); + std::string resolved_upbc_name = + upbc::ResolveFieldName(field, upbc_field_names); + WriteFieldAccessorHazzer(desc, field, resolved_field_name, + resolved_upbc_name, output); + WriteFieldAccessorClear(desc, field, resolved_field_name, + resolved_upbc_name, output); + + if (field->is_map()) { + WriteMapFieldAccessors(desc, field, resolved_field_name, + resolved_upbc_name, output); + } else if (desc->options().map_entry()) { + // TODO Implement map entry + } else if (field->is_repeated()) { + WriteRepeatedFieldsInMessageHeader(desc, field, resolved_field_name, + resolved_upbc_name, output); + } else { + // non-repeated. + if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) { + output(R"cc( + $0 $1() const; + void set_$1($0 value); + )cc", + CppConstType(field), resolved_field_name); + } else if (field->cpp_type() == + protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + output(R"cc( + $1 $2() const; + $0 mutable_$2(); + )cc", + MessagePtrConstType(field, /* const */ false), + MessagePtrConstType(field, /* const */ true), + resolved_field_name, resolved_upbc_name); + } else { + output( + R"cc( + inline $0 $1() const { return $2_$3(msg_); } + inline void set_$1($0 value) { return $2_set_$3(msg_, value); } + )cc", + CppConstType(field), resolved_field_name, MessageName(desc), + resolved_upbc_name); + } + } + } +} + +void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const absl::string_view resolved_upbc_name, + Output& output) { + // Generate hazzer (if any). + if (field->has_presence()) { + // Has presence. + output("inline bool has_$0() const { return $1_has_$2(msg_); }\n", + resolved_field_name, MessageName(desc), resolved_upbc_name); + } +} + +void WriteFieldAccessorClear(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const absl::string_view resolved_upbc_name, + Output& output) { + if (field->has_presence()) { + output("void clear_$0() { $2_clear_$1(msg_); }\n", resolved_field_name, + resolved_upbc_name, MessageName(desc)); + } +} + +void WriteMapFieldAccessors(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const absl::string_view resolved_upbc_name, + Output& output) { + const protobuf::Descriptor* entry = field->message_type(); + const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1); + const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2); + output( + R"cc( + inline size_t $0_size() const { return $1_$3_size(msg_); } + inline void clear_$0() { $1_clear_$3(msg_); } + void delete_$0($2 key); + )cc", + resolved_field_name, MessageName(desc), CppConstType(key), + resolved_upbc_name); + + if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + output( + R"cc( + bool set_$0($1 key, $3 value); + bool set_$0($1 key, $4 value); + absl::StatusOr<$3> get_$0($1 key); + )cc", + resolved_field_name, CppConstType(key), CppConstType(val), + MessagePtrConstType(val, /* is_const */ true), + MessagePtrConstType(val, /* is_const */ false)); + } else { + output( + R"cc( + bool set_$0($1 key, $2 value); + absl::StatusOr<$2> get_$0($1 key); + )cc", + resolved_field_name, CppConstType(key), CppConstType(val)); + } +} + +void WriteAccessorsInSource(const protobuf::Descriptor* desc, Output& output) { + std::string class_name = ClassName(desc); + absl::StrAppend(&class_name, "Access"); + output("namespace internal {\n"); + const char arena_expression[] = "arena_"; + auto field_names = CreateFieldNameMap(desc); + auto upbc_field_names = upbc::CreateFieldNameMap(desc); + + // Generate const methods. + OutputIndenter i(output); + for (const auto* field : FieldNumberOrder(desc)) { + std::string resolved_field_name = ResolveFieldName(field, field_names); + std::string resolved_upbc_name = + upbc::ResolveFieldName(field, upbc_field_names); + if (field->is_map()) { + WriteMapAccessorDefinitions(desc, field, resolved_field_name, class_name, + output); + } else if (desc->options().map_entry()) { + // TODO Implement map entry + } else if (field->is_repeated()) { + if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + WriteRepeatedMessageAccessor(desc, field, resolved_field_name, + class_name, output); + } else if (field->cpp_type() == + protobuf::FieldDescriptor::CPPTYPE_STRING) { + WriteRepeatedStringAccessor(desc, field, resolved_field_name, + class_name, output); + } else { + WriteRepeatedScalarAccessor(desc, field, resolved_field_name, + class_name, output); + } + } else { + // non-repeated field. + if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) { + output( + R"cc( + $1 $0::$2() const { + return ::protos::UpbStrToStringView($3_$4(msg_)); + } + )cc", + class_name, CppConstType(field), resolved_field_name, + MessageName(desc), resolved_upbc_name); + // Set string. + output( + R"cc( + void $0::set_$2($1 value) { + $4_set_$3(msg_, ::protos::UpbStrFromStringView(value, $5)); + } + )cc", + class_name, CppConstType(field), resolved_field_name, + resolved_upbc_name, MessageName(desc), arena_expression); + } else if (field->cpp_type() == + protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + output( + R"cc( + $1 $0::$2() const { + if (!has_$2()) { + return $4::default_instance(); + } + return ::protos::internal::CreateMessage<$4>( + (upb_Message*)($3_$5(msg_)), arena_); + } + )cc", + class_name, MessagePtrConstType(field, /* is_const */ true), + resolved_field_name, MessageName(desc), + MessageBaseType(field, /* maybe_const */ false), + resolved_upbc_name); + + output( + R"cc( + $1 $0::mutable_$2() { + return ::protos::internal::CreateMessageProxy<$4>( + (upb_Message*)($3_mutable_$5(msg_, $6)), $6); + } + )cc", + class_name, MessagePtrConstType(field, /* is_const */ false), + resolved_field_name, MessageName(desc), + MessageBaseType(field, /* maybe_const */ false), resolved_upbc_name, + arena_expression); + } + } + } + output("\n"); + output("} // namespace internal\n\n"); +} + +void WriteMapAccessorDefinitions(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const absl::string_view class_name, + Output& output) { + const protobuf::Descriptor* entry = field->message_type(); + const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1); + const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2); + absl::string_view upbc_name = field->name(); + absl::string_view converted_key_name = "key"; + absl::string_view optional_conversion_code = ""; + + if (key->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) { + // Insert conversion from absl::string_view to upb_StringView. + // Creates upb_StringView on stack to prevent allocation. + converted_key_name = "upb_key"; + optional_conversion_code = + "upb_StringView upb_key = {key.data(), key.size()};\n"; + } + if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + output( + R"cc( + bool $0::set_$1($2 key, $3 value) { + upb_Message* clone = upb_Message_DeepClone( + ::protos::internal::PrivateAccess::GetInternalMsg(value), &$9, + arena_); + $6return $4_$8_set(msg_, $7, ($5*)clone, arena_); + } + )cc", + class_name, resolved_field_name, CppConstType(key), + MessagePtrConstType(val, /* is_const */ true), MessageName(message), + MessageName(val->message_type()), optional_conversion_code, + converted_key_name, upbc_name, + ::upbc::MessageInit(val->message_type()->full_name())); + output( + R"cc( + bool $0::set_$1($2 key, $3 value) { + upb_Message* clone = upb_Message_DeepClone( + ::protos::internal::PrivateAccess::GetInternalMsg(value), &$9, + arena_); + $6return $4_$8_set(msg_, $7, ($5*)clone, arena_); + } + )cc", + class_name, resolved_field_name, CppConstType(key), + MessagePtrConstType(val, /* is_const */ false), MessageName(message), + MessageName(val->message_type()), optional_conversion_code, + converted_key_name, upbc_name, + ::upbc::MessageInit(val->message_type()->full_name())); + output( + R"cc( + absl::StatusOr<$3> $0::get_$1($2 key) { + $5* msg_value; + $7bool success = $4_$9_get(msg_, $8, &msg_value); + if (success) { + return ::protos::internal::CreateMessage<$6>(msg_value, arena_); + } + return absl::NotFoundError(""); + } + )cc", + class_name, resolved_field_name, CppConstType(key), + MessagePtrConstType(val, /* is_const */ true), MessageName(message), + MessageName(val->message_type()), + QualifiedClassName(val->message_type()), optional_conversion_code, + converted_key_name, upbc_name); + output( + R"cc( + void $0::delete_$1($2 key) { $6$4_$8_delete(msg_, $7); } + )cc", + class_name, resolved_field_name, CppConstType(key), + MessagePtrConstType(val, /* is_const */ false), MessageName(message), + MessageName(val->message_type()), optional_conversion_code, + converted_key_name, upbc_name); + } else if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) { + output( + R"cc( + bool $0::set_$1($2 key, $3 value) { + $5return $4_$7_set(msg_, $6, + ::protos::UpbStrFromStringView(value, arena_), + arena_); + } + )cc", + class_name, resolved_field_name, CppConstType(key), CppConstType(val), + MessageName(message), optional_conversion_code, converted_key_name, + upbc_name); + output( + R"cc( + absl::StatusOr<$3> $0::get_$1($2 key) { + upb_StringView value; + $5bool success = $4_$7_get(msg_, $6, &value); + if (success) { + return absl::string_view(value.data, value.size); + } + return absl::NotFoundError(""); + } + )cc", + class_name, resolved_field_name, CppConstType(key), CppConstType(val), + MessageName(message), optional_conversion_code, converted_key_name, + upbc_name); + output( + R"cc( + void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); } + )cc", + class_name, resolved_field_name, CppConstType(key), CppConstType(val), + MessageName(message), optional_conversion_code, converted_key_name, + upbc_name); + } else { + output( + R"cc( + bool $0::set_$1($2 key, $3 value) { + $5return $4_$7_set(msg_, $6, value, arena_); + } + )cc", + class_name, resolved_field_name, CppConstType(key), CppConstType(val), + MessageName(message), optional_conversion_code, converted_key_name, + upbc_name); + output( + R"cc( + absl::StatusOr<$3> $0::get_$1($2 key) { + $3 value; + $5bool success = $4_$7_get(msg_, $6, &value); + if (success) { + return value; + } + return absl::NotFoundError(""); + } + )cc", + class_name, resolved_field_name, CppConstType(key), CppConstType(val), + MessageName(message), optional_conversion_code, converted_key_name, + upbc_name); + output( + R"cc( + void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); } + )cc", + class_name, resolved_field_name, CppConstType(key), CppConstType(val), + MessageName(message), optional_conversion_code, converted_key_name, + upbc_name); + } +} + +void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc, + MessageClassType handle_type, Output& output) { + bool read_only = handle_type == MessageClassType::kMessageCProxy; + + // Generate const methods. + OutputIndenter i(output); + std::string class_name = ClassName(desc); + auto field_names = CreateFieldNameMap(desc); + + for (const auto* field : FieldNumberOrder(desc)) { + std::string resolved_field_name = ResolveFieldName(field, field_names); + // Generate hazzer (if any). + if (field->has_presence()) { + output("using $0Access::has_$1;\n", class_name, resolved_field_name); + if (!read_only) { + output("using $0Access::clear_$1;\n", class_name, resolved_field_name); + } + } + if (field->is_map()) { + output( + R"cc( + using $0Access::$1_size; + using $0Access::get_$1; + )cc", + class_name, resolved_field_name); + if (!read_only) { + output( + R"cc( + using $0Access::clear_$1; + using $0Access::delete_$1; + using $0Access::set_$1; + )cc", + class_name, resolved_field_name); + } + } else if (desc->options().map_entry()) { + // TODO Implement map entry + } else if (field->is_repeated()) { + WriteRepeatedFieldUsingAccessors(field, class_name, resolved_field_name, + output, read_only); + } else { + if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + output("using $0Access::$1;\n", ClassName(desc), resolved_field_name); + if (!read_only) { + output("using $0Access::mutable_$1;\n", class_name, + resolved_field_name); + } + } else { + output("using $0Access::$1;\n", class_name, resolved_field_name); + if (!read_only) { + output("using $0Access::set_$1;\n", class_name, resolved_field_name); + } + } + } + } + for (int i = 0; i < desc->real_oneof_decl_count(); ++i) { + const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i); + output("using $0Access::$1_case;\n", class_name, oneof->name()); + output("using $0Access::$1Case;\n", class_name, + ToCamelCase(oneof->name(), /*lower_first=*/false)); + for (int j = 0; j < oneof->field_count(); ++j) { + const protobuf::FieldDescriptor* field = oneof->field(j); + output("using $0Access::k$1;\n", class_name, + ToCamelCase(field->name(), /*lower_first=*/false), + field->number()); + } + output("using $0Access::$1_NOT_SET;\n", class_name, + absl::AsciiStrToUpper(oneof->name())); + } +} + +void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc, + Output& output) { + // Generate const methods. + OutputIndenter i(output); + std::string class_name = ClassName(desc); + auto field_names = CreateFieldNameMap(desc); + for (int i = 0; i < desc->real_oneof_decl_count(); ++i) { + const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i); + output("enum $0Case {\n", + ToCamelCase(oneof->name(), /*lower_first=*/false)); + for (int j = 0; j < oneof->field_count(); ++j) { + const protobuf::FieldDescriptor* field = oneof->field(j); + output(" k$0 = $1,\n", ToCamelCase(field->name(), /*lower_first=*/false), + field->number()); + } + output(" $0_NOT_SET = 0,\n", absl::AsciiStrToUpper(oneof->name())); + output("};\n\n"); + output("$0Case $1_case() const {\n", + ToCamelCase(oneof->name(), /*lower_first=*/false), oneof->name()); + for (int j = 0; j < oneof->field_count(); ++j) { + const protobuf::FieldDescriptor* field = oneof->field(j); + std::string resolved_field_name = ResolveFieldName(field, field_names); + output(" if (has_$0()) { return k$1; }\n", resolved_field_name, + ToCamelCase(field->name(), /*lower_first=*/false)); + } + output(" return $0_NOT_SET;\n", absl::AsciiStrToUpper(oneof->name())); + output("}\n;"); + } +} + +std::string ResolveFieldName(const protobuf::FieldDescriptor* field, + const NameToFieldDescriptorMap& field_names) { + // C++ implementation specific reserved names. + static const auto& kReservedNames = + *new absl::flat_hash_set<absl::string_view>({ + "msg", + "msg_", + "arena", + "arena_", + }); + + // C++ specific prefixes used by code generator for field access. + static constexpr absl::string_view kClearMethodPrefix = "clear_"; + static constexpr absl::string_view kSetMethodPrefix = "set_"; + static constexpr absl::string_view kHasMethodPrefix = "has_"; + static constexpr absl::string_view kDeleteMethodPrefix = "delete_"; + static constexpr absl::string_view kAddToRepeatedMethodPrefix = "add_"; + static constexpr absl::string_view kResizeArrayMethodPrefix = "resize_"; + + // List of generated accessor prefixes to check against. + // Example: + // optional repeated string phase = 236; + // optional bool clear_phase = 237; + static constexpr absl::string_view kAccessorPrefixes[] = { + kClearMethodPrefix, kDeleteMethodPrefix, kAddToRepeatedMethodPrefix, + kResizeArrayMethodPrefix, kSetMethodPrefix, kHasMethodPrefix}; + + absl::string_view field_name = field->name(); + if (kReservedNames.count(field_name) > 0) { + if (absl::EndsWith(field_name, "_")) { + return absl::StrCat(field_name, "_"); + } else { + return absl::StrCat(field_name, "__"); + } + } + for (const auto prefix : kAccessorPrefixes) { + // If field name starts with a prefix such as clear_ and the proto + // contains a field name with trailing end, depending on type of field + // (repeated, map, message) we have a conflict to resolve. + if (absl::StartsWith(field_name, prefix)) { + auto match = field_names.find(field_name.substr(prefix.size())); + if (match != field_names.end()) { + const auto* candidate = match->second; + if (candidate->is_repeated() || candidate->is_map() || + (candidate->cpp_type() == + protobuf::FieldDescriptor::CPPTYPE_STRING && + prefix == kClearMethodPrefix) || + prefix == kSetMethodPrefix || prefix == kHasMethodPrefix) { + return absl::StrCat(field_name, "_"); + } + } + } + } + return upbc::ResolveKeywordConflict(std::string(field_name)); +} + +} // namespace protos_generator
diff --git a/protos_generator/gen_accessors.h b/protos_generator/gen_accessors.h new file mode 100644 index 0000000..8372fc3 --- /dev/null +++ b/protos_generator/gen_accessors.h
@@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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_PROTOS_GENERATOR_ACCESSORS_H_ +#define UPB_PROTOS_GENERATOR_ACCESSORS_H_ + +#include "google/protobuf/descriptor.h" +#include "protos_generator/gen_utils.h" +#include "protos_generator/output.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc, + Output& output); +void WriteAccessorsInSource(const protobuf::Descriptor* desc, Output& output); +void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc, + MessageClassType handle_type, Output& output); +void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc, + Output& output); +} // namespace protos_generator + +#endif // UPB_PROTOS_GENERATOR_ACCESSORS_H_
diff --git a/protos_generator/gen_enums.cc b/protos_generator/gen_enums.cc new file mode 100644 index 0000000..7c535a7 --- /dev/null +++ b/protos_generator/gen_enums.cc
@@ -0,0 +1,144 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 "protos_generator/gen_enums.h" + +#include <algorithm> +#include <limits> +#include <string> +#include <vector> + +#include "google/protobuf/descriptor.pb.h" +#include "google/protobuf/descriptor.h" +#include "protos_generator/gen_utils.h" +#include "protos_generator/names.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +// Convert enum value to C++ literal. +// +// In C++, an value of -2147483648 gets interpreted as the negative of +// 2147483648, and since 2147483648 can't fit in an integer, this produces a +// compiler warning. This works around that issue. +std::string EnumInt32ToString(int number) { + if (number == std::numeric_limits<int32_t>::min()) { + // This needs to be special-cased, see explanation here: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661 + return absl::StrCat(number + 1, " - 1"); + } else { + return absl::StrCat(number); + } +} + +std::string EnumTypeName(const protobuf::EnumDescriptor* enum_descriptor) { + auto containing_type = enum_descriptor->containing_type(); + if (containing_type == nullptr) { + // enums types with no package name are prefixed with protos_ to prevent + // conflicts with generated C headers. + if (enum_descriptor->file()->package().empty()) { + return absl::StrCat(kNoPackageNamePrefix, + ToCIdent(enum_descriptor->name())); + } + return ToCIdent(enum_descriptor->name()); + } else { + // Since the enum is in global name space (no package), it will have the + // same classified name as the C header include, to prevent collision + // rename as above. + if (containing_type->file()->package().empty()) { + return ToCIdent(absl::StrCat(containing_type->name(), "_", + kNoPackageNamePrefix, + enum_descriptor->name())); + } else { + return ToCIdent( + absl::StrCat(containing_type->name(), "_", enum_descriptor->name())); + } + } +} + +std::string EnumValueSymbolInNameSpace( + const protobuf::EnumDescriptor* desc, + const protobuf::EnumValueDescriptor* value) { + auto containing_type = desc->containing_type(); + if (containing_type != nullptr) { + return ToCIdent(absl::StrCat(containing_type->name(), "_", desc->name(), + "_", value->name())); + } else { + // protos enum values with no package name are prefixed with protos_ to + // prevent conflicts with generated C headers. + if (desc->file()->package().empty()) { + return absl::StrCat(kNoPackageNamePrefix, ToCIdent(value->name())); + } + return ToCIdent(value->name()); + } +} + +void WriteEnumValues(const protobuf::EnumDescriptor* desc, Output& output) { + std::vector<const protobuf::EnumValueDescriptor*> values; + auto value_count = desc->value_count(); + values.reserve(value_count); + for (int i = 0; i < value_count; i++) { + values.push_back(desc->value(i)); + } + std::sort(values.begin(), values.end(), + [](const protobuf::EnumValueDescriptor* a, + const protobuf::EnumValueDescriptor* b) { + return a->number() < b->number(); + }); + + for (size_t i = 0; i < values.size(); i++) { + auto value = values[i]; + output(" $0", EnumValueSymbolInNameSpace(desc, value)); + output(" = $0", EnumInt32ToString(value->number())); + if (i != values.size() - 1) { + output(","); + } + output("\n"); + } +} + +void WriteEnumDeclarations( + const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) { + for (auto enumdesc : enums) { + output("enum $0 : int {\n", EnumTypeName(enumdesc)); + WriteEnumValues(enumdesc, output); + output("};\n\n"); + } +} + +void WriteHeaderEnumForwardDecls( + std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) { + for (const auto* enumdesc : enums) { + output("enum $0 : int;\n", EnumTypeName(enumdesc)); + } +} + +} // namespace protos_generator
diff --git a/protos_generator/gen_enums.h b/protos_generator/gen_enums.h new file mode 100644 index 0000000..6078762 --- /dev/null +++ b/protos_generator/gen_enums.h
@@ -0,0 +1,52 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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_PROTOS_GENERATOR_ENUMS_H_ +#define UPB_PROTOS_GENERATOR_ENUMS_H_ + +#include "google/protobuf/descriptor.h" +#include "protos_generator/output.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +std::string EnumTypeName(const protobuf::EnumDescriptor* enum_descriptor); +std::string EnumValueSymbolInNameSpace( + const protobuf::EnumDescriptor* desc, + const protobuf::EnumValueDescriptor* value); +void WriteHeaderEnumForwardDecls( + std::vector<const protobuf::EnumDescriptor*>& enums, Output& output); +void WriteEnumDeclarations( + const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output); + +} // namespace protos_generator + +#endif // UPB_PROTOS_GENERATOR_ENUMS_H_
diff --git a/protos_generator/gen_extensions.cc b/protos_generator/gen_extensions.cc new file mode 100644 index 0000000..be0cbc5 --- /dev/null +++ b/protos_generator/gen_extensions.cc
@@ -0,0 +1,117 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 "protos_generator/gen_extensions.h" + +#include "absl/strings/str_cat.h" +#include "protos_generator/gen_utils.h" +#include "protos_generator/names.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +std::string ExtensionIdentifierBase(const protobuf::FieldDescriptor* ext) { + assert(ext->is_extension()); + std::string ext_scope; + if (ext->extension_scope()) { + return MessageName(ext->extension_scope()); + } else { + return ToCIdent(ext->file()->package()); + } +} + +std::string ContainingTypeName(const protobuf::FieldDescriptor* ext) { + return ext->containing_type()->file() != ext->file() + ? QualifiedClassName(ext->containing_type()) + : ClassName(ext->containing_type()); +} + +void WriteExtensionIdentifierHeader(const protobuf::FieldDescriptor* ext, + Output& output) { + std::string mini_table_name = + absl::StrCat(ExtensionIdentifierBase(ext), "_", ext->name(), "_ext"); + if (ext->extension_scope()) { + output( + R"cc( + static const ::protos::internal::ExtensionIdentifier<$0, $1> $2; + )cc", + ContainingTypeName(ext), CppTypeParameterName(ext), ext->name()); + } else { + output( + R"cc( + extern const ::protos::internal::ExtensionIdentifier<$0, $1> $2; + )cc", + ContainingTypeName(ext), CppTypeParameterName(ext), ext->name()); + } +} + +void WriteExtensionIdentifiersHeader( + const std::vector<const protobuf::FieldDescriptor*>& extensions, + Output& output) { + for (const auto* ext : extensions) { + if (!ext->extension_scope()) { + WriteExtensionIdentifierHeader(ext, output); + } + } +} + +void WriteExtensionIdentifier(const protobuf::FieldDescriptor* ext, + Output& output) { + std::string mini_table_name = + absl::StrCat(ExtensionIdentifierBase(ext), "_", ext->name(), "_ext"); + if (ext->extension_scope()) { + output( + R"cc( + const ::protos::internal::ExtensionIdentifier<$0, $3> $4::$2(&$1); + )cc", + ContainingTypeName(ext), mini_table_name, ext->name(), + CppTypeParameterName(ext), ClassName(ext->extension_scope())); + } else { + output( + R"cc( + const ::protos::internal::ExtensionIdentifier<$0, $3> $2(&$1); + )cc", + ContainingTypeName(ext), mini_table_name, ext->name(), + CppTypeParameterName(ext)); + } +} + +void WriteExtensionIdentifiers( + const std::vector<const protobuf::FieldDescriptor*>& extensions, + Output& output) { + for (const auto* ext : extensions) { + if (!ext->extension_scope()) { + WriteExtensionIdentifier(ext, output); + } + } +} + +} // namespace protos_generator
diff --git a/protos_generator/gen_extensions.h b/protos_generator/gen_extensions.h new file mode 100644 index 0000000..8ec740a --- /dev/null +++ b/protos_generator/gen_extensions.h
@@ -0,0 +1,54 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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_PROTOS_GENERATOR_GEN_EXTENSIONS_H_ +#define UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_ + +#include "google/protobuf/descriptor.h" +#include "protos_generator/output.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +void WriteExtensionIdentifiersHeader( + const std::vector<const protobuf::FieldDescriptor*>& extensions, + Output& output); +void WriteExtensionIdentifierHeader(const protobuf::FieldDescriptor* ext, + Output& output); +void WriteExtensionIdentifiers( + const std::vector<const protobuf::FieldDescriptor*>& extensions, + Output& output); +void WriteExtensionIdentifier(const protobuf::FieldDescriptor* ext, + Output& output); + +} // namespace protos_generator + +#endif // UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_
diff --git a/protos_generator/gen_messages.cc b/protos_generator/gen_messages.cc new file mode 100644 index 0000000..ca75ba7 --- /dev/null +++ b/protos_generator/gen_messages.cc
@@ -0,0 +1,507 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 "protos_generator/gen_messages.h" + +#include <string> +#include <vector> + +#include "google/protobuf/descriptor.pb.h" +#include "absl/strings/str_cat.h" +#include "google/protobuf/descriptor.h" +#include "protos_generator/gen_accessors.h" +#include "protos_generator/gen_enums.h" +#include "protos_generator/gen_extensions.h" +#include "protos_generator/gen_utils.h" +#include "protos_generator/names.h" +#include "protos_generator/output.h" +#include "upbc/common.h" +#include "upbc/file_layout.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor, + Output& output); +void WriteModelPublicDeclaration( + const protobuf::Descriptor* descriptor, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + const std::vector<const protobuf::EnumDescriptor*>& file_enums, + Output& output); +void WriteExtensionIdentifiersInClassHeader( + const protobuf::Descriptor* message, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + Output& output); +void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor, + Output& output); +void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor, + Output& output); +void WriteInternalForwardDeclarationsInHeader( + const protobuf::Descriptor* message, Output& output); +void WriteDefaultInstanceHeader(const protobuf::Descriptor* message, + Output& output); +void WriteExtensionIdentifiersImplementation( + const protobuf::Descriptor* message, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + Output& output); +void WriteUsingEnumsInHeader( + const protobuf::Descriptor* message, + const std::vector<const protobuf::EnumDescriptor*>& file_enums, + Output& output); + +// Writes message class declarations into .upb.proto.h. +// +// For each proto Foo, FooAccess and FooProxy/FooCProxy are generated +// that are exposed to users as Foo , Ptr<Foo> and Ptr<const Foo>. +void WriteMessageClassDeclarations( + const protobuf::Descriptor* descriptor, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + const std::vector<const protobuf::EnumDescriptor*>& file_enums, + Output& output) { + if (IsMapEntryMessage(descriptor)) { + // Skip map entry generation. Low level accessors for maps are + // generated that don't require a separate map type. + return; + } + + // Forward declaration of Proto Class for GCC handling of free friend method. + output("class $0;\n", ClassName(descriptor)); + output("namespace internal {\n\n"); + WriteModelAccessDeclaration(descriptor, output); + output("\n"); + WriteInternalForwardDeclarationsInHeader(descriptor, output); + output("\n"); + output("} // namespace internal\n\n"); + WriteModelPublicDeclaration(descriptor, file_exts, file_enums, output); + output("namespace internal {\n"); + WriteModelCProxyDeclaration(descriptor, output); + WriteModelProxyDeclaration(descriptor, output); + output("} // namespace internal\n\n"); +} + +void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor, + Output& output) { + output( + R"cc( + class $0Access { + public: + $0Access() {} + $0Access($1* msg, upb_Arena* arena) : msg_(msg), arena_(arena) { + assert(arena != nullptr); + } // NOLINT + $0Access(const $1* msg, upb_Arena* arena) + : msg_(const_cast<$1*>(msg)), arena_(arena) { + assert(arena != nullptr); + } // NOLINT + void* GetInternalArena() const { return arena_; } + )cc", + ClassName(descriptor), MessageName(descriptor)); + WriteFieldAccessorsInHeader(descriptor, output); + WriteOneofAccessorsInHeader(descriptor, output); + output.Indent(); + output( + R"cc( + private: + friend class $2; + friend class $0Proxy; + friend class $0CProxy; + friend struct ::protos::internal::PrivateAccess; + $1* msg_; + upb_Arena* arena_; + )cc", + ClassName(descriptor), MessageName(descriptor), + QualifiedClassName(descriptor)); + output.Outdent(); + output("};\n"); +} + +void WriteModelPublicDeclaration( + const protobuf::Descriptor* descriptor, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + const std::vector<const protobuf::EnumDescriptor*>& file_enums, + Output& output) { + output( + R"cc( + class $0 final : private internal::$0Access { + public: + using Access = internal::$0Access; + using Proxy = internal::$0Proxy; + using CProxy = internal::$0CProxy; + + $0(); + + $0(const $0& from); + $0& operator=(const $3& from); + $0(const CProxy& from); + $0(const Proxy& from); + $0& operator=(const CProxy& from); + + $0($0&& m) + : Access(absl::exchange(m.msg_, nullptr), + absl::exchange(m.arena_, nullptr)), + owned_arena_(std::move(m.owned_arena_)) {} + + $0& operator=($0&& m) { + msg_ = absl::exchange(m.msg_, nullptr); + arena_ = absl::exchange(m.arena_, nullptr); + owned_arena_ = std::move(m.owned_arena_); + return *this; + } + )cc", + ClassName(descriptor), ::upbc::MessageInit(descriptor->full_name()), + MessageName(descriptor), QualifiedClassName(descriptor)); + + WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessage, output); + WriteUsingEnumsInHeader(descriptor, file_enums, output); + WriteDefaultInstanceHeader(descriptor, output); + WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output); + if (descriptor->extension_range_count()) { + // for typetrait checking + output("using ExtendableType = $0;\n", ClassName(descriptor)); + } + // Note: free function friends that are templates such as ::protos::Parse + // require explicit <$2> type parameter in declaration to be able to compile + // with gcc otherwise the compiler will fail with + // "has not been declared within namespace" error. Even though there is a + // namespace qualifier, cross namespace matching fails. + output.Indent(); + output( + R"cc( + static const upb_MiniTable* minitable(); + using $0Access::GetInternalArena; + )cc", + ClassName(descriptor)); + output("\n"); + output( + R"cc( + private: + const void* msg() const { return msg_; } + void* msg() { return msg_; } + + $0(upb_Message* msg, upb_Arena* arena) : $0Access() { + msg_ = ($1*)msg; + arena_ = owned_arena_.ptr(); + upb_Arena_Fuse(arena_, arena); + } + ::protos::Arena owned_arena_; + friend struct ::protos::internal::PrivateAccess; + friend Proxy; + friend CProxy; + friend absl::StatusOr<$2>(::protos::Parse<$2>(absl::string_view bytes, + int options)); + friend absl::StatusOr<$2>(::protos::Parse<$2>( + absl::string_view bytes, + const ::protos::ExtensionRegistry& extension_registry, + int options)); + friend upb_Arena* ::protos::internal::GetArena<$0>($0* message); + friend upb_Arena* ::protos::internal::GetArena<$0>(::protos::Ptr<$0> message); + friend $0(::protos::internal::MoveMessage<$0>(upb_Message* msg, + upb_Arena* arena)); + )cc", + ClassName(descriptor), MessageName(descriptor), + QualifiedClassName(descriptor)); + output.Outdent(); + output("};\n\n"); +} + +void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor, + Output& output) { + // Foo::Proxy. + output( + R"cc( + class $0Proxy final : private internal::$0Access { + public: + $0Proxy() = delete; + $0Proxy(const $0Proxy& m) : internal::$0Access() { + msg_ = m.msg_; + arena_ = m.arena_; + } + $0Proxy($0* m) : internal::$0Access() { + msg_ = m->msg_; + arena_ = m->arena_; + } + $0Proxy operator=(const $0Proxy& m) { + msg_ = m.msg_; + arena_ = m.arena_; + return *this; + } + using $0Access::GetInternalArena; + )cc", + ClassName(descriptor)); + + WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy, + output); + output("\n"); + output.Indent(1); + output( + R"cc( + private: + void* msg() const { return msg_; } + + $0Proxy(void* msg, upb_Arena* arena) : internal::$0Access(($1*)msg, arena) {} + friend $0::Proxy(::protos::CreateMessage<$0>(::protos::Arena& arena)); + friend $0::Proxy(::protos::internal::CreateMessageProxy<$0>( + upb_Message*, upb_Arena*)); + friend struct ::protos::internal::PrivateAccess; + friend class RepeatedFieldProxy; + friend class $0CProxy; + friend class $0Access; + friend class ::protos::Ptr<$0>; + friend class ::protos::Ptr<const $0>; + static const upb_MiniTable* minitable() { return $0::minitable(); } + friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0Proxy>( + const $0Proxy* message); + friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0Proxy>( + ::protos::Ptr<$0Proxy> message); + friend upb_Arena* ::protos::internal::GetArena<$2>($2* message); + friend upb_Arena* ::protos::internal::GetArena<$2>(::protos::Ptr<$2> message); + static void Rebind($0Proxy& lhs, const $0Proxy& rhs) { + lhs.msg_ = rhs.msg_; + lhs.arena_ = rhs.arena_; + } + )cc", + ClassName(descriptor), MessageName(descriptor), + QualifiedClassName(descriptor)); + output.Outdent(1); + output("};\n\n"); +} + +void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor, + Output& output) { + // Foo::CProxy. + output( + R"cc( + class $0CProxy final : private internal::$0Access { + public: + $0CProxy() = delete; + $0CProxy(const $0* m) + : internal::$0Access(m->msg_, ::protos::internal::GetArena(m)) {} + $0CProxy($0Proxy m); + using $0Access::GetInternalArena; + )cc", + ClassName(descriptor), MessageName(descriptor)); + + WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageCProxy, + output); + + output.Indent(1); + output( + R"cc( + private: + using AsNonConst = $0Proxy; + const void* msg() const { return msg_; } + + $0CProxy(const void* msg, upb_Arena* arena) + : internal::$0Access(($1*)msg, arena){}; + friend struct ::protos::internal::PrivateAccess; + friend class RepeatedFieldProxy; + friend class ::protos::Ptr<$0>; + friend class ::protos::Ptr<const $0>; + static const upb_MiniTable* minitable() { return $0::minitable(); } + friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0CProxy>( + const $0CProxy* message); + friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0CProxy>( + ::protos::Ptr<$0CProxy> message); + + static void Rebind($0CProxy& lhs, const $0CProxy& rhs) { + lhs.msg_ = rhs.msg_; + lhs.arena_ = rhs.arena_; + } + )cc", + ClassName(descriptor), MessageName(descriptor)); + output.Outdent(1); + output("};\n\n"); +} + +void WriteDefaultInstanceHeader(const protobuf::Descriptor* message, + Output& output) { + output(" static ::protos::Ptr<const $0> default_instance();\n", + ClassName(message)); +} + +void WriteMessageImplementation( + const protobuf::Descriptor* descriptor, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + Output& output) { + bool message_is_map_entry = descriptor->options().map_entry(); + if (!message_is_map_entry) { + // Constructor. + output( + R"cc( + $0::$0() : $0Access() { + arena_ = owned_arena_.ptr(); + msg_ = $1_new(arena_); + } + $0::$0(const $0& from) : $0Access() { + arena_ = owned_arena_.ptr(); + msg_ = ($1*)::protos::internal::DeepClone(from.msg_, &$2, arena_); + } + $0::$0(const CProxy& from) : $0Access() { + arena_ = owned_arena_.ptr(); + msg_ = ($1*)::protos::internal::DeepClone( + ::protos::internal::GetInternalMsg(&from), &$2, arena_); + } + $0::$0(const Proxy& from) : $0(static_cast<const CProxy&>(from)) {} + internal::$0CProxy::$0CProxy($0Proxy m) : $0Access() { + arena_ = m.arena_; + msg_ = ($1*)::protos::internal::GetInternalMsg(&m); + } + $0& $0::operator=(const $3& from) { + arena_ = owned_arena_.ptr(); + msg_ = ($1*)::protos::internal::DeepClone(from.msg_, &$2, arena_); + return *this; + } + $0& $0::operator=(const CProxy& from) { + arena_ = owned_arena_.ptr(); + msg_ = ($1*)::protos::internal::DeepClone( + ::protos::internal::GetInternalMsg(&from), &$2, arena_); + return *this; + } + )cc", + ClassName(descriptor), MessageName(descriptor), + ::upbc::MessageInit(descriptor->full_name()), + QualifiedClassName(descriptor)); + output("\n"); + // Minitable + output( + R"cc( + const upb_MiniTable* $0::minitable() { return &$1; } + )cc", + ClassName(descriptor), ::upbc::MessageInit(descriptor->full_name())); + output("\n"); + } + + WriteAccessorsInSource(descriptor, output); + + if (!message_is_map_entry) { + output( + R"cc( + struct $0DefaultTypeInternal { + $1* msg; + upb_Arena* arena; + }; + static $0DefaultTypeInternal _$0DefaultTypeBuilder() { + upb_Arena* arena = upb_Arena_New(); + return $0DefaultTypeInternal{$1_new(arena), arena}; + } + $0DefaultTypeInternal _$0_default_instance_ = _$0DefaultTypeBuilder(); + )cc", + ClassName(descriptor), MessageName(descriptor)); + + output( + R"cc( + ::protos::Ptr<const $0> $0::default_instance() { + return ::protos::internal::CreateMessage<$0>( + (upb_Message *)_$0_default_instance_.msg, + _$0_default_instance_.arena); + } + )cc", + ClassName(descriptor)); + + WriteExtensionIdentifiersImplementation(descriptor, file_exts, output); + } +} + +void WriteInternalForwardDeclarationsInHeader( + const protobuf::Descriptor* message, Output& output) { + // Write declaration for internal re-usable default_instance without + // leaking implementation. + output( + R"cc( + struct $0DefaultTypeInternal; + extern $0DefaultTypeInternal _$0_default_instance_; + )cc", + ClassName(message)); +} + +void WriteExtensionIdentifiersInClassHeader( + const protobuf::Descriptor* message, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + Output& output) { + for (auto* ext : file_exts) { + if (ext->extension_scope() && + ext->extension_scope()->full_name() == message->full_name()) { + WriteExtensionIdentifierHeader(ext, output); + } + } +} + +void WriteExtensionIdentifiersImplementation( + const protobuf::Descriptor* message, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + Output& output) { + for (auto* ext : file_exts) { + if (ext->extension_scope() && + ext->extension_scope()->full_name() == message->full_name()) { + WriteExtensionIdentifier(ext, output); + } + } +} + +void WriteUsingEnumsInHeader( + const protobuf::Descriptor* message, + const std::vector<const protobuf::EnumDescriptor*>& file_enums, + Output& output) { + for (auto* enum_descriptor : file_enums) { + std::string enum_type_name = EnumTypeName(enum_descriptor); + std::string enum_resolved_type_name = + enum_descriptor->file()->package().empty() && + enum_descriptor->containing_type() == nullptr + ? absl::StrCat(kNoPackageNamePrefix, + ToCIdent(enum_descriptor->name())) + : enum_type_name; + if (enum_descriptor->containing_type() == nullptr || + enum_descriptor->containing_type()->full_name() != + message->full_name()) { + continue; + } + output("using $0", enum_descriptor->name()); + if (enum_descriptor->options().deprecated()) { + output(" ABSL_DEPRECATED(\"Proto enum $0\")", enum_descriptor->name()); + } + output(" = $0;", enum_resolved_type_name); + output("\n"); + int value_count = enum_descriptor->value_count(); + for (int i = 0; i < value_count; i++) { + output("static constexpr $0 $1", enum_descriptor->name(), + enum_descriptor->value(i)->name()); + if (enum_descriptor->options().deprecated() || + enum_descriptor->value(i)->options().deprecated()) { + output(" ABSL_DEPRECATED(\"Proto enum value $0\") ", + enum_descriptor->value(i)->name()); + } + output(" = $0;\n", EnumValueSymbolInNameSpace(enum_descriptor, + enum_descriptor->value(i))); + } + } +} + +} // namespace protos_generator
diff --git a/protos_generator/gen_messages.h b/protos_generator/gen_messages.h new file mode 100644 index 0000000..3236964 --- /dev/null +++ b/protos_generator/gen_messages.h
@@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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_PROTOS_GENERATOR_GEN_MESSAGES_H_ +#define UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_ + +#include "google/protobuf/descriptor.h" +#include "protos_generator/output.h" + +namespace protos_generator { +namespace protobuf = ::google::protobuf; + +void WriteMessageClassDeclarations( + const protobuf::Descriptor* descriptor, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + const std::vector<const protobuf::EnumDescriptor*>& file_enums, + Output& output); +void WriteMessageImplementation( + const protobuf::Descriptor* descriptor, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + Output& output); +} // namespace protos_generator + +#endif // UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_
diff --git a/protos_generator/gen_repeated_fields.cc b/protos_generator/gen_repeated_fields.cc new file mode 100644 index 0000000..d5b4bab --- /dev/null +++ b/protos_generator/gen_repeated_fields.cc
@@ -0,0 +1,347 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 "protos_generator/gen_repeated_fields.h" + +#include <string> +#include <vector> + +#include "google/protobuf/descriptor.pb.h" +#include "absl/strings/string_view.h" +#include "google/protobuf/descriptor.h" +#include "protos_generator/gen_accessors.h" +#include "protos_generator/gen_enums.h" +#include "protos_generator/gen_extensions.h" +#include "protos_generator/gen_utils.h" +#include "protos_generator/names.h" +#include "protos_generator/output.h" +#include "upbc/common.h" +#include "upbc/file_layout.h" +#include "upbc/names.h" + +namespace protos_generator { +namespace protobuf = ::google::protobuf; + +// Adds using accessors to reuse base Access class members from a Proxy/CProxy. +void WriteRepeatedFieldUsingAccessors(const protobuf::FieldDescriptor* field, + absl::string_view class_name, + absl::string_view resolved_field_name, + Output& output, bool read_only) { + if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + output( + R"cc( + using $0Access::$1; + using $0Access::$1_size; + )cc", + class_name, resolved_field_name); + if (!read_only) { + output( + R"cc( + using $0Access::add_$1; + using $0Access::mutable_$1; + )cc", + class_name, resolved_field_name); + } + } else { + output( + R"cc( + using $0Access::$1; + using $0Access::$1_size; + )cc", + class_name, resolved_field_name); + if (!read_only) { + output( + R"cc( + using $0Access::add_$1; + using $0Access::mutable_$1; + using $0Access::resize_$1; + using $0Access::set_$1; + )cc", + class_name, resolved_field_name); + } + } +} + +void WriteRepeatedFieldsInMessageHeader(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + absl::string_view resolved_field_name, + absl::string_view resolved_upbc_name, + Output& output) { + output( + R"cc( + inline size_t $1_size() const { + size_t len; + $0_$2(msg_, &len); + return len; + } + )cc", + MessageName(desc), resolved_field_name, resolved_upbc_name); + + if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + output( + R"cc( + $1 $2(size_t index) const; + const ::protos::RepeatedField<const $4>::CProxy $2() const; + ::protos::Ptr<::protos::RepeatedField<$4>> mutable_$2(); + absl::StatusOr<$0> add_$2(); + $0 mutable_$2(size_t index) const; + )cc", + MessagePtrConstType(field, /* const */ false), // $0 + MessagePtrConstType(field, /* const */ true), // $1 + resolved_field_name, // $2 + resolved_upbc_name, // $3 + MessageBaseType(field, /* maybe_const */ false) // $4 + ); + } else if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) { + output( + R"cc( + $0 $1(size_t index) const; + const ::protos::RepeatedField<$0>::CProxy $1() const; + ::protos::Ptr<::protos::RepeatedField<$0>> mutable_$1(); + bool add_$1($0 val); + void set_$1(size_t index, $0 val); + bool resize_$1(size_t len); + )cc", + CppConstType(field), resolved_field_name); + } else { + output( + R"cc( + $0 $1(size_t index) const; + const ::protos::RepeatedField<$0>::CProxy $1() const; + ::protos::Ptr<::protos::RepeatedField<$0>> mutable_$1(); + bool add_$1($0 val); + void set_$1(size_t index, $0 val); + bool resize_$1(size_t len); + )cc", + CppConstType(field), resolved_field_name); + } +} + +void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const absl::string_view class_name, + Output& output) { + const char arena_expression[] = "arena_"; + absl::string_view upbc_name = field->name(); + output( + R"cc( + $1 $0::$2(size_t index) const { + size_t len; + auto* ptr = $3_$5(msg_, &len); + assert(index < len); + return ::protos::internal::CreateMessage<$4>( + (upb_Message*)*(ptr + index), arena_); + } + )cc", + class_name, MessagePtrConstType(field, /* is_const */ true), + resolved_field_name, MessageName(message), + MessageBaseType(field, /* maybe_const */ false), upbc_name); + output( + R"cc( + absl::StatusOr<$1> $0::add_$2() { + auto new_msg = $3_add_$6(msg_, $5); + if (!new_msg) { + return ::protos::MessageAllocationError(); + } + return ::protos::internal::CreateMessageProxy<$4>((upb_Message*)new_msg, $5); + } + )cc", + class_name, MessagePtrConstType(field, /* const */ false), + resolved_field_name, MessageName(message), + MessageBaseType(field, /* maybe_const */ false), arena_expression, + upbc_name); + output( + R"cc( + $1 $0::mutable_$2(size_t index) const { + size_t len; + auto* ptr = $3_$6(msg_, &len); + assert(index < len); + return ::protos::internal::CreateMessageProxy<$4>( + (upb_Message*)*(ptr + index), $5); + } + )cc", + class_name, MessagePtrConstType(field, /* is_const */ false), + resolved_field_name, MessageName(message), + MessageBaseType(field, /* maybe_const */ false), arena_expression, + upbc_name); + output( + R"cc( + const ::protos::RepeatedField<const $1>::CProxy $0::$2() const { + size_t size; + const upb_Array* arr = _$3_$4_$5(msg_, &size); + return ::protos::RepeatedField<const $1>::CProxy(arr, arena_); + }; + ::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() { + size_t size; + upb_Array* arr = _$3_$4_$6(msg_, &size, arena_); + return ::protos::RepeatedField<$1>::Proxy(arr, arena_); + } + )cc", + class_name, // $0 + MessageBaseType(field, /* maybe_const */ false), // $1 + resolved_field_name, // $2 + MessageName(message), // $3 + upbc_name, // $4 + upbc::kRepeatedFieldArrayGetterPostfix, // $5 + upbc::kRepeatedFieldMutableArrayGetterPostfix // $6 + ); +} + +void WriteRepeatedStringAccessor(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const absl::string_view class_name, + Output& output) { + absl::string_view upbc_name = field->name(); + output( + R"cc( + $1 $0::$2(size_t index) const { + size_t len; + auto* ptr = $3_mutable_$4(msg_, &len); + assert(index < len); + return ::protos::UpbStrToStringView(*(ptr + index)); + } + )cc", + class_name, CppConstType(field), resolved_field_name, + MessageName(message), upbc_name); + output( + R"cc( + bool $0::resize_$1(size_t len) { + return $2_resize_$3(msg_, len, arena_); + } + )cc", + class_name, resolved_field_name, MessageName(message), upbc_name); + output( + R"cc( + bool $0::add_$2($1 val) { + return $3_add_$4(msg_, ::protos::UpbStrFromStringView(val, arena_), arena_); + } + )cc", + class_name, CppConstType(field), resolved_field_name, + MessageName(message), upbc_name); + output( + R"cc( + void $0::set_$2(size_t index, $1 val) { + size_t len; + auto* ptr = $3_mutable_$4(msg_, &len); + assert(index < len); + *(ptr + index) = ::protos::UpbStrFromStringView(val, arena_); + } + )cc", + class_name, CppConstType(field), resolved_field_name, + MessageName(message), upbc_name); + output( + R"cc( + const ::protos::RepeatedField<$1>::CProxy $0::$2() const { + size_t size; + const upb_Array* arr = _$3_$4_$5(msg_, &size); + return ::protos::RepeatedField<$1>::CProxy(arr, arena_); + }; + ::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() { + size_t size; + upb_Array* arr = _$3_$4_$6(msg_, &size, arena_); + return ::protos::RepeatedField<$1>::Proxy(arr, arena_); + } + )cc", + class_name, // $0 + CppConstType(field), // $1 + resolved_field_name, // $2 + MessageName(message), // $3 + upbc_name, // $4 + upbc::kRepeatedFieldArrayGetterPostfix, // $5 + upbc::kRepeatedFieldMutableArrayGetterPostfix // $6 + ); +} + +void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const absl::string_view class_name, + Output& output) { + absl::string_view upbc_name = field->name(); + output( + R"cc( + $1 $0::$2(size_t index) const { + size_t len; + auto* ptr = $3_mutable_$4(msg_, &len); + assert(index < len); + return *(ptr + index); + } + )cc", + class_name, CppConstType(field), resolved_field_name, + MessageName(message), upbc_name); + output( + R"cc( + bool $0::resize_$1(size_t len) { + return $2_resize_$3(msg_, len, arena_); + } + )cc", + class_name, resolved_field_name, MessageName(message), upbc_name); + output( + R"cc( + bool $0::add_$2($1 val) { return $3_add_$4(msg_, val, arena_); } + )cc", + class_name, CppConstType(field), resolved_field_name, + MessageName(message), upbc_name); + output( + R"cc( + void $0::set_$2(size_t index, $1 val) { + size_t len; + auto* ptr = $3_mutable_$4(msg_, &len); + assert(index < len); + *(ptr + index) = val; + } + )cc", + class_name, CppConstType(field), resolved_field_name, + MessageName(message), upbc_name); + output( + R"cc( + const ::protos::RepeatedField<$1>::CProxy $0::$2() const { + size_t size; + const upb_Array* arr = _$3_$4_$5(msg_, &size); + return ::protos::RepeatedField<$1>::CProxy(arr, arena_); + }; + ::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() { + size_t size; + upb_Array* arr = _$3_$4_$6(msg_, &size, arena_); + return ::protos::RepeatedField<$1>::Proxy(arr, arena_); + } + )cc", + class_name, // $0 + CppConstType(field), // $1 + resolved_field_name, // $2 + MessageName(message), // $3 + upbc_name, // $4 + upbc::kRepeatedFieldArrayGetterPostfix, // $5 + upbc::kRepeatedFieldMutableArrayGetterPostfix // $6 + ); +} + +} // namespace protos_generator
diff --git a/protos_generator/gen_repeated_fields.h b/protos_generator/gen_repeated_fields.h new file mode 100644 index 0000000..1650eb0 --- /dev/null +++ b/protos_generator/gen_repeated_fields.h
@@ -0,0 +1,69 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 THIRD_PARTY_UPB_PROTOS_GENERATOR_GEN_REPEATED_FIELDS_H_ +#define THIRD_PARTY_UPB_PROTOS_GENERATOR_GEN_REPEATED_FIELDS_H_ + +#include "absl/strings/string_view.h" +#include "google/protobuf/descriptor.h" +#include "protos_generator/output.h" + +namespace protos_generator { +namespace protobuf = ::google::protobuf; + +void WriteRepeatedFieldUsingAccessors(const protobuf::FieldDescriptor* field, + absl::string_view class_name, + absl::string_view resolved_field_name, + Output& output, bool read_only); + +void WriteRepeatedFieldsInMessageHeader(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + absl::string_view resolved_field_name, + absl::string_view resolved_upbc_name, + Output& output); + +void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + absl::string_view resolved_field_name, + absl::string_view class_name, Output& output); + +void WriteRepeatedStringAccessor(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + absl::string_view resolved_field_name, + absl::string_view class_name, Output& output); + +void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + absl::string_view resolved_field_name, + absl::string_view class_name, Output& output); + +} // namespace protos_generator + +#endif // THIRD_PARTY_UPB_PROTOS_GENERATOR_GEN_REPEATED_FIELDS_H_
diff --git a/protos_generator/gen_utils.cc b/protos_generator/gen_utils.cc new file mode 100644 index 0000000..79dd5a1 --- /dev/null +++ b/protos_generator/gen_utils.cc
@@ -0,0 +1,152 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 "protos_generator/gen_utils.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "absl/strings/ascii.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +void AddEnums(const protobuf::Descriptor* message, + std::vector<const protobuf::EnumDescriptor*>* enums) { + enums->reserve(enums->size() + message->enum_type_count()); + for (int i = 0; i < message->enum_type_count(); i++) { + enums->push_back(message->enum_type(i)); + } + for (int i = 0; i < message->nested_type_count(); i++) { + AddEnums(message->nested_type(i), enums); + } +} + +std::vector<const protobuf::EnumDescriptor*> SortedEnums( + const protobuf::FileDescriptor* file) { + std::vector<const protobuf::EnumDescriptor*> enums; + enums.reserve(file->enum_type_count()); + for (int i = 0; i < file->enum_type_count(); i++) { + enums.push_back(file->enum_type(i)); + } + for (int i = 0; i < file->message_type_count(); i++) { + AddEnums(file->message_type(i), &enums); + } + return enums; +} + +void AddMessages(const protobuf::Descriptor* message, + std::vector<const protobuf::Descriptor*>* messages) { + messages->push_back(message); + for (int i = 0; i < message->nested_type_count(); i++) { + AddMessages(message->nested_type(i), messages); + } +} + +std::vector<const protobuf::Descriptor*> SortedMessages( + const protobuf::FileDescriptor* file) { + std::vector<const protobuf::Descriptor*> messages; + for (int i = 0; i < file->message_type_count(); i++) { + AddMessages(file->message_type(i), &messages); + } + return messages; +} + +void AddExtensionsFromMessage( + const protobuf::Descriptor* message, + std::vector<const protobuf::FieldDescriptor*>* exts) { + for (int i = 0; i < message->extension_count(); i++) { + exts->push_back(message->extension(i)); + } + for (int i = 0; i < message->nested_type_count(); i++) { + AddExtensionsFromMessage(message->nested_type(i), exts); + } +} + +std::vector<const protobuf::FieldDescriptor*> SortedExtensions( + const protobuf::FileDescriptor* file) { + const int extension_count = file->extension_count(); + const int message_type_count = file->message_type_count(); + + std::vector<const protobuf::FieldDescriptor*> ret; + ret.reserve(extension_count + message_type_count); + + for (int i = 0; i < extension_count; i++) { + ret.push_back(file->extension(i)); + } + for (int i = 0; i < message_type_count; i++) { + AddExtensionsFromMessage(file->message_type(i), &ret); + } + + return ret; +} + +std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder( + const protobuf::Descriptor* message) { + std::vector<const protobuf::FieldDescriptor*> fields; + fields.reserve(message->field_count()); + for (int i = 0; i < message->field_count(); i++) { + fields.push_back(message->field(i)); + } + std::sort(fields.begin(), fields.end(), + [](const protobuf::FieldDescriptor* a, + const protobuf::FieldDescriptor* b) { + return a->number() < b->number(); + }); + return fields; +} + +std::string ToCamelCase(const std::string& input, bool lower_first) { + bool capitalize_next = !lower_first; + std::string result; + result.reserve(input.size()); + + for (char character : input) { + if (character == '_') { + capitalize_next = true; + } else if (capitalize_next) { + result.push_back(absl::ascii_toupper(character)); + capitalize_next = false; + } else { + result.push_back(character); + } + } + + // Lower-case the first letter. + if (lower_first && !result.empty()) { + result[0] = absl::ascii_tolower(result[0]); + } + + return result; +} + +} // namespace protos_generator
diff --git a/protos_generator/gen_utils.h b/protos_generator/gen_utils.h new file mode 100644 index 0000000..71f325f --- /dev/null +++ b/protos_generator/gen_utils.h
@@ -0,0 +1,68 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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_PROTOS_GENERATOR_GEN_UTILS_H_ +#define UPB_PROTOS_GENERATOR_GEN_UTILS_H_ + +#include <string> +#include <vector> + +#include "google/protobuf/descriptor.pb.h" +#include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/descriptor.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +enum class MessageClassType { + kMessage, + kMessageCProxy, + kMessageProxy, + kMessageAccess, +}; + +inline bool IsMapEntryMessage(const protobuf::Descriptor* descriptor) { + return descriptor->options().map_entry(); +} +std::vector<const protobuf::EnumDescriptor*> SortedEnums( + const protobuf::FileDescriptor* file); +std::vector<const protobuf::Descriptor*> SortedMessages( + const protobuf::FileDescriptor* file); +std::vector<const protobuf::FieldDescriptor*> SortedExtensions( + const protobuf::FileDescriptor* file); +std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder( + const protobuf::Descriptor* message); + +std::string ToCamelCase(const std::string& input, bool lower_first); + +} // namespace protos_generator + +#endif // UPB_PROTOS_GENERATOR_GEN_UTILS_H_
diff --git a/protos_generator/names.cc b/protos_generator/names.cc new file mode 100644 index 0000000..eb3fbee --- /dev/null +++ b/protos_generator/names.cc
@@ -0,0 +1,201 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 "protos_generator/names.h" + +#include <string> + +#include "upbc/keywords.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +namespace { + +std::string NamespaceFromPackageName(absl::string_view package_name) { + return absl::StrCat(absl::StrReplaceAll(package_name, {{".", "::"}}), + "::protos"); +} + +std::string DotsToColons(const std::string& name) { + return absl::StrReplaceAll(name, {{".", "::"}}); +} + +std::string Namespace(const std::string& package) { + if (package.empty()) return ""; + return "::" + DotsToColons(package); +} + +// Return the qualified C++ name for a file level symbol. +std::string QualifiedFileLevelSymbol(const protobuf::FileDescriptor* file, + const std::string& name) { + if (file->package().empty()) { + return absl::StrCat("::", name); + } + // Append ::protos postfix to package name. + return absl::StrCat(Namespace(file->package()), "::protos::", name); +} + +std::string CppTypeInternal(const protobuf::FieldDescriptor* field, + bool is_const, bool is_type_parameter) { + std::string maybe_const = is_const ? "const " : ""; + switch (field->cpp_type()) { + case protobuf::FieldDescriptor::CPPTYPE_MESSAGE: { + if (is_type_parameter) { + return absl::StrCat(maybe_const, + QualifiedClassName(field->message_type())); + } else { + return absl::StrCat(maybe_const, + QualifiedClassName(field->message_type()), "*"); + } + } + case protobuf::FieldDescriptor::CPPTYPE_BOOL: + return "bool"; + case protobuf::FieldDescriptor::CPPTYPE_FLOAT: + return "float"; + case protobuf::FieldDescriptor::CPPTYPE_INT32: + case protobuf::FieldDescriptor::CPPTYPE_ENUM: + return "int32_t"; + case protobuf::FieldDescriptor::CPPTYPE_UINT32: + return "uint32_t"; + case protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + return "double"; + case protobuf::FieldDescriptor::CPPTYPE_INT64: + return "int64_t"; + case protobuf::FieldDescriptor::CPPTYPE_UINT64: + return "uint64_t"; + case protobuf::FieldDescriptor::CPPTYPE_STRING: + return "absl::string_view"; + default: + ABSL_LOG(FATAL) << "Unexpected type: " << field->cpp_type(); + } +} + +} // namespace + +std::string ClassName(const protobuf::Descriptor* descriptor) { + const protobuf::Descriptor* parent = descriptor->containing_type(); + std::string res; + // Classes in global namespace without package names are prefixed + // by protos_ to avoid collision with C compiler structs defined in + // proto.upb.h. + if ((parent && parent->file()->package().empty()) || + descriptor->file()->package().empty()) { + res = std::string(kNoPackageNamePrefix); + } + if (parent) res += ClassName(parent) + "_"; + absl::StrAppend(&res, descriptor->name()); + return ::upbc::ResolveKeywordConflict(res); +} + +std::string QualifiedClassName(const protobuf::Descriptor* descriptor) { + return QualifiedFileLevelSymbol(descriptor->file(), ClassName(descriptor)); +} + +std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor) { + return QualifiedFileLevelSymbol( + descriptor->file(), absl::StrCat("internal::", ClassName(descriptor))); +} + +std::string CppSourceFilename(const google::protobuf::FileDescriptor* file) { + return StripExtension(file->name()) + ".upb.proto.cc"; +} + +std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file) { + return StripExtension(file->name()) + ".upb.fwd.h"; +} + +std::string UpbCFilename(const google::protobuf::FileDescriptor* file) { + return StripExtension(file->name()) + ".upb.h"; +} + +std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file) { + return StripExtension(file->name()) + ".upb.proto.h"; +} + +void WriteStartNamespace(const protobuf::FileDescriptor* file, Output& output) { + // Skip namespace generation if package name is not specified. + if (file->package().empty()) { + return; + } + + output("namespace $0 {\n\n", NamespaceFromPackageName(file->package())); +} + +void WriteEndNamespace(const protobuf::FileDescriptor* file, Output& output) { + if (file->package().empty()) { + return; + } + output("} // namespace $0\n\n", NamespaceFromPackageName(file->package())); +} + +std::string CppConstType(const protobuf::FieldDescriptor* field) { + return CppTypeInternal(field, /* is_const= */ true, + /* is_type_parameter= */ false); +} + +std::string CppTypeParameterName(const protobuf::FieldDescriptor* field) { + return CppTypeInternal(field, /* is_const= */ false, + /* is_type_parameter= */ true); +} + +std::string MessageBaseType(const protobuf::FieldDescriptor* field, + bool is_const) { + ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE); + std::string maybe_const = is_const ? "const " : ""; + return maybe_const + QualifiedClassName(field->message_type()); +} + +std::string MessagePtrConstType(const protobuf::FieldDescriptor* field, + bool is_const) { + ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE); + std::string maybe_const = is_const ? "const " : ""; + return "::protos::Ptr<" + maybe_const + + QualifiedClassName(field->message_type()) + ">"; +} + +std::string MessageCProxyType(const protobuf::FieldDescriptor* field, + bool is_const) { + ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE); + std::string maybe_const = is_const ? "const " : ""; + return maybe_const + QualifiedInternalClassName(field->message_type()) + + "CProxy"; +} + +std::string MessageProxyType(const protobuf::FieldDescriptor* field, + bool is_const) { + ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE); + std::string maybe_const = is_const ? "const " : ""; + return maybe_const + QualifiedInternalClassName(field->message_type()) + + "Proxy"; +} + +} // namespace protos_generator
diff --git a/protos_generator/names.h b/protos_generator/names.h new file mode 100644 index 0000000..efa01ce --- /dev/null +++ b/protos_generator/names.h
@@ -0,0 +1,73 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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_PROTOS_GENERATOR_NAMES_H_ +#define UPB_PROTOS_GENERATOR_NAMES_H_ + +#include <string> + +#include "google/protobuf/descriptor.pb.h" +#include "protos_generator/output.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +inline constexpr absl::string_view kNoPackageNamePrefix = "protos_"; + +std::string ClassName(const protobuf::Descriptor* descriptor); +std::string QualifiedClassName(const protobuf::Descriptor* descriptor); +std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor); + +std::string CppSourceFilename(const google::protobuf::FileDescriptor* file); +std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file); +std::string UpbCFilename(const google::protobuf::FileDescriptor* file); +std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file); + +void WriteStartNamespace(const protobuf::FileDescriptor* file, Output& output); +void WriteEndNamespace(const protobuf::FileDescriptor* file, Output& output); + +std::string CppConstType(const protobuf::FieldDescriptor* field); +std::string CppTypeParameterName(const protobuf::FieldDescriptor* field); + +std::string MessageBaseType(const protobuf::FieldDescriptor* field, + bool is_const); +// Generate protos::Ptr<const Model> to be used in accessors as public +// signatures. +std::string MessagePtrConstType(const protobuf::FieldDescriptor* field, + bool is_const); +std::string MessageCProxyType(const protobuf::FieldDescriptor* field, + bool is_const); +std::string MessageProxyType(const protobuf::FieldDescriptor* field, + bool is_const); + +} // namespace protos_generator + +#endif // UPB_PROTOS_GENERATOR_NAMES_H_
diff --git a/protos_generator/output.cc b/protos_generator/output.cc new file mode 100644 index 0000000..72cd4e5 --- /dev/null +++ b/protos_generator/output.cc
@@ -0,0 +1,92 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 "protos_generator/output.h" + +#include <string> + +#include "absl/strings/str_replace.h" + +namespace protos_generator { +namespace { + +namespace protobuf = ::google::protobuf; + +} // namespace + +std::string StripExtension(absl::string_view fname) { + size_t lastdot = fname.find_last_of('.'); + if (lastdot == std::string::npos) { + return std::string(fname); + } + return std::string(fname.substr(0, lastdot)); +} + +std::string ToCIdent(absl::string_view str) { + return absl::StrReplaceAll(str, {{".", "_"}, {"/", "_"}, {"-", "_"}}); +} + +std::string ToPreproc(absl::string_view str) { + return absl::AsciiStrToUpper(ToCIdent(str)); +} + +void EmitFileWarning(const protobuf::FileDescriptor* file, Output& output) { + output( + R"cc( + /* This file was generated by protos_generator (the upb C++ compiler) " + from the input + * file: + * + * $0 + * + * Do not edit -- your changes will be discarded when the file is + * regenerated. */ + )cc", + file->name()); + output("\n"); +} + +std::string MessageName(const protobuf::Descriptor* descriptor) { + return ToCIdent(descriptor->full_name()); +} + +std::string FileLayoutName(const google::protobuf::FileDescriptor* file) { + return ToCIdent(file->name()) + "_upb_file_layout"; +} + +std::string CHeaderFilename(const google::protobuf::FileDescriptor* file) { + return StripExtension(file->name()) + ".upb.h"; +} + +std::string CSourceFilename(const google::protobuf::FileDescriptor* file) { + return StripExtension(file->name()) + ".upb.c"; +} + +} // namespace protos_generator
diff --git a/protos_generator/output.h b/protos_generator/output.h new file mode 100644 index 0000000..53f0720 --- /dev/null +++ b/protos_generator/output.h
@@ -0,0 +1,174 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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_PROTOS_GENERATOR_OUTPUT_H +#define UPB_PROTOS_GENERATOR_OUTPUT_H + +#include <vector> + +#include "absl/log/absl_log.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/substitute.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/io/zero_copy_stream.h" + +namespace protos_generator { + +class Output { + public: + Output(google::protobuf::io::ZeroCopyOutputStream* stream) : stream_(stream) {} + ~Output() { stream_->BackUp((int)buffer_size_); } + + template <class... Arg> + void operator()(absl::string_view format, const Arg&... arg) { + Write(absl::Substitute(format, arg...)); + } + + // Indentation size in characters. + static constexpr size_t kIndentationSize = 2; + + void Indent() { Indent(kIndentationSize); } + void Indent(size_t size) { indent_ += size; } + + void Outdent() { Outdent(kIndentationSize); } + void Outdent(size_t size) { + if (indent_ < size) { + ABSL_LOG(FATAL) << "mismatched Output indent/unindent calls"; + } + indent_ -= size; + } + + private: + void Write(absl::string_view data) { + std::string stripped; + if (absl::StartsWith(data, "\n ")) { + size_t indent = data.substr(1).find_first_not_of(' '); + if (indent > indent_) { + indent -= indent_; + } + if (indent != absl::string_view::npos) { + // Remove indentation from all lines. + auto line_prefix = data.substr(0, indent + 1); + // The final line has an extra newline and is indented two less, eg. + // R"cc( + // UPB_INLINE $0 $1_$2(const $1 *msg) { + // return $1_has_$2(msg) ? *UPB_PTR_AT(msg, $3, $0) : $4; + // } + // )cc", + std::string last_line_prefix = std::string(line_prefix); + last_line_prefix.resize(last_line_prefix.size() - 2); + data.remove_prefix(line_prefix.size()); + stripped = absl::StrReplaceAll( + data, {{line_prefix, "\n"}, {last_line_prefix, "\n"}}); + data = stripped; + } + } else { + WriteIndent(); + } + WriteRaw(data); + } + + void WriteRaw(absl::string_view data) { + while (!data.empty()) { + RefreshOutput(); + size_t to_write = std::min(data.size(), buffer_size_); + memcpy(output_buffer_, data.data(), to_write); + data.remove_prefix(to_write); + output_buffer_ += to_write; + buffer_size_ -= to_write; + } + } + + void WriteIndent() { + if (indent_ == 0) { + return; + } + size_t size = indent_; + while (size > buffer_size_) { + if (buffer_size_ > 0) { + memset(output_buffer_, ' ', buffer_size_); + } + size -= buffer_size_; + buffer_size_ = 0; + RefreshOutput(); + } + memset(output_buffer_, ' ', size); + output_buffer_ += size; + buffer_size_ -= size; + } + + void RefreshOutput() { + while (buffer_size_ == 0) { + void* void_buffer; + int size; + if (!stream_->Next(&void_buffer, &size)) { + fprintf(stderr, "upbc: Failed to write to to output\n"); + abort(); + } + output_buffer_ = static_cast<char*>(void_buffer); + buffer_size_ = size; + } + } + + google::protobuf::io::ZeroCopyOutputStream* stream_; + char* output_buffer_ = nullptr; + size_t buffer_size_ = 0; + // Current indentation size in characters. + size_t indent_ = 0; + friend class OutputIndenter; +}; + +class OutputIndenter { + public: + OutputIndenter(Output& output) + : OutputIndenter(output, Output::kIndentationSize) {} + OutputIndenter(Output& output, size_t indent_size) + : indent_size_(indent_size), output_(output) { + output.Indent(indent_size); + } + ~OutputIndenter() { output_.Outdent(indent_size_); } + + private: + size_t indent_size_; + Output& output_; +}; + +std::string StripExtension(absl::string_view fname); +std::string ToCIdent(absl::string_view str); +std::string ToPreproc(absl::string_view str); +void EmitFileWarning(const google::protobuf::FileDescriptor* file, Output& output); +std::string MessageName(const google::protobuf::Descriptor* descriptor); +std::string FileLayoutName(const google::protobuf::FileDescriptor* file); +std::string CHeaderFilename(const google::protobuf::FileDescriptor* file); +std::string CSourceFilename(const google::protobuf::FileDescriptor* file); + +} // namespace protos_generator + +#endif // UPB_PROTOS_GENERATOR_OUTPUT_H
diff --git a/protos_generator/protoc-gen-upb-protos.cc b/protos_generator/protoc-gen-upb-protos.cc new file mode 100644 index 0000000..58c3712 --- /dev/null +++ b/protos_generator/protoc-gen-upb-protos.cc
@@ -0,0 +1,282 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 <memory> + +#include "google/protobuf/descriptor.pb.h" +#include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/plugin.h" +#include "google/protobuf/descriptor.h" +#include "protos_generator/gen_enums.h" +#include "protos_generator/gen_extensions.h" +#include "protos_generator/gen_messages.h" +#include "protos_generator/gen_utils.h" +#include "protos_generator/names.h" +#include "protos_generator/output.h" +#include "upbc/file_layout.h" + +namespace protos_generator { +namespace { + +namespace protoc = ::google::protobuf::compiler; +namespace protobuf = ::google::protobuf; +using FileDescriptor = ::google::protobuf::FileDescriptor; + +void WriteSource(const protobuf::FileDescriptor* file, Output& output, + bool fasttable_enabled); +void WriteHeader(const protobuf::FileDescriptor* file, Output& output); +void WriteForwardingHeader(const protobuf::FileDescriptor* file, + Output& output); +void WriteMessageImplementations(const protobuf::FileDescriptor* file, + Output& output); +void WriteTypedefForwardingHeader( + const protobuf::FileDescriptor* file, + const std::vector<const protobuf::Descriptor*>& file_messages, + Output& output); +void WriteHeaderMessageForwardDecls( + const protobuf::FileDescriptor* file, + Output& output); + +class Generator : public protoc::CodeGenerator { + public: + ~Generator() override {} + bool Generate(const protobuf::FileDescriptor* file, + const std::string& parameter, protoc::GeneratorContext* context, + std::string* error) const override; + uint64_t GetSupportedFeatures() const override { + return FEATURE_PROTO3_OPTIONAL; + } +}; + +bool Generator::Generate(const protobuf::FileDescriptor* file, + const std::string& parameter, + protoc::GeneratorContext* context, + std::string* error) const { + bool fasttable_enabled = false; + std::vector<std::pair<std::string, std::string>> params; + google::protobuf::compiler::ParseGeneratorParameter(parameter, ¶ms); + + for (const auto& pair : params) { + if (pair.first == "fasttable") { + fasttable_enabled = true; + } else { + *error = "Unknown parameter: " + pair.first; + return false; + } + } + + // Write model.upb.fwd.h + Output forwarding_header_output( + context->Open(ForwardingHeaderFilename(file))); + WriteForwardingHeader(file, forwarding_header_output); + // Write model.upb.proto.h + Output header_output(context->Open(CppHeaderFilename(file))); + WriteHeader(file, header_output); + // Write model.upb.proto.cc + Output cc_output(context->Open(CppSourceFilename(file))); + WriteSource(file, cc_output, fasttable_enabled); + return true; +} + +// The forwarding header defines Access/Proxy/CProxy for message classes +// used to include when referencing dependencies to prevent transitive +// dependency headers from being included. +void WriteForwardingHeader(const protobuf::FileDescriptor* file, + Output& output) { + EmitFileWarning(file, output); + output( + R"cc( +#ifndef $0_UPB_FWD_H_ +#define $0_UPB_FWD_H_ + )cc", + ToPreproc(file->name())); + output("\n"); + for (int i = 0; i < file->public_dependency_count(); ++i) { + output("#include \"$0\"\n", + ForwardingHeaderFilename(file->public_dependency(i))); + } + if (file->public_dependency_count() > 0) { + output("\n"); + } + const std::vector<const protobuf::Descriptor*> this_file_messages = + SortedMessages(file); + WriteTypedefForwardingHeader(file, this_file_messages, output); + output("#endif /* $0_UPB_FWD_H_ */\n", ToPreproc(file->name())); +} + +void WriteHeader(const protobuf::FileDescriptor* file, Output& output) { + EmitFileWarning(file, output); + output( + R"cc( +#ifndef $0_UPB_PROTO_H_ +#define $0_UPB_PROTO_H_ + +#include "protos/protos.h" +#include "protos/protos_internal.h" +#include "protos/repeated_field.h" + +#include "absl/strings/string_view.h" +#include "absl/status/statusor.h" + )cc", + ToPreproc(file->name())); + + // Import headers for proto public dependencies. + for (int i = 0; i < file->public_dependency_count(); i++) { + if (i == 0) { + output("// Public Imports.\n"); + } + output("#include \"$0\"\n", CppHeaderFilename(file->public_dependency(i))); + if (i == file->public_dependency_count() - 1) { + output("\n"); + } + } + + output("#include \"upb/port/def.inc\"\n"); + + const std::vector<const protobuf::Descriptor*> this_file_messages = + SortedMessages(file); + const std::vector<const protobuf::FieldDescriptor*> this_file_exts = + SortedExtensions(file); + + if (!this_file_messages.empty()) { + output("\n"); + } + + WriteHeaderMessageForwardDecls(file, output); + WriteStartNamespace(file, output); + + std::vector<const protobuf::EnumDescriptor*> this_file_enums = + SortedEnums(file); + + // Write Class and Enums. + WriteEnumDeclarations(this_file_enums, output); + output("\n"); + + for (auto message : this_file_messages) { + WriteMessageClassDeclarations(message, this_file_exts, this_file_enums, + output); + } + output("\n"); + + WriteExtensionIdentifiersHeader(this_file_exts, output); + output("\n"); + + WriteEndNamespace(file, output); + + output("\n#include \"upb/port/undef.inc\"\n\n"); + // End of "C" section. + + output("#endif /* $0_UPB_PROTO_H_ */\n", ToPreproc(file->name())); +} + +// Writes a .upb.cc source file. +void WriteSource(const protobuf::FileDescriptor* file, Output& output, + bool fasttable_enabled) { + EmitFileWarning(file, output); + + output( + R"cc( +#include <stddef.h> +#include "absl/strings/string_view.h" +#include "protos/protos.h" +#include "$0" + )cc", + CppHeaderFilename(file)); + + for (int i = 0; i < file->dependency_count(); i++) { + output("#include \"$0\"\n", CppHeaderFilename(file->dependency(i))); + } + output("#include \"upb/port/def.inc\"\n"); + + WriteStartNamespace(file, output); + WriteMessageImplementations(file, output); + const std::vector<const protobuf::FieldDescriptor*> this_file_exts = + SortedExtensions(file); + WriteExtensionIdentifiers(this_file_exts, output); + WriteEndNamespace(file, output); + + output("#include \"upb/port/undef.inc\"\n\n"); +} + +void WriteMessageImplementations(const protobuf::FileDescriptor* file, + Output& output) { + const std::vector<const protobuf::FieldDescriptor*> file_exts = + SortedExtensions(file); + const std::vector<const protobuf::Descriptor*> this_file_messages = + SortedMessages(file); + for (auto message : this_file_messages) { + WriteMessageImplementation(message, file_exts, output); + } +} + +void WriteTypedefForwardingHeader( + const protobuf::FileDescriptor* file, + const std::vector<const protobuf::Descriptor*>& file_messages, + Output& output) { + WriteStartNamespace(file, output); + + // Forward-declare types defined in this file. + for (auto message : file_messages) { + output( + R"cc( + class $0; + namespace internal { + class $0Access; + class $0Proxy; + class $0CProxy; + } // namespace internal + )cc", + ClassName(message)); + } + output("\n"); + WriteEndNamespace(file, output); +} + +/// Writes includes for upb C minitables and fwd.h for transitive typedefs. +void WriteHeaderMessageForwardDecls( + const protobuf::FileDescriptor* file, + Output& output) { + // Import forward-declaration of types defined in this file. + output("#include \"$0\"\n", UpbCFilename(file)); + output("#include \"$0\"\n", ForwardingHeaderFilename(file)); + // Import forward-declaration of types in dependencies. + for (int i = 0; i < file->dependency_count(); ++i) { + output("#include \"$0\"\n", ForwardingHeaderFilename(file->dependency(i))); + } + output("\n"); +} + +} // namespace +} // namespace protos_generator + +int main(int argc, char** argv) { + protos_generator::Generator generator_cc; + return google::protobuf::compiler::PluginMain(argc, argv, &generator_cc); +}
diff --git a/protos_generator/tests/BUILD b/protos_generator/tests/BUILD new file mode 100644 index 0000000..e5c203f --- /dev/null +++ b/protos_generator/tests/BUILD
@@ -0,0 +1,140 @@ +# Copyright (c) 2009-2021, Google LLC +# All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +load( + "//bazel:build_defs.bzl", + "UPB_DEFAULT_CPPOPTS", +) +load( + "//bazel:upb_proto_library.bzl", + "upb_proto_library", +) +load( + "//protos/bazel:upb_cc_proto_library.bzl", + "upb_cc_proto_library", +) +load( + "@rules_cc//cc:defs.bzl", + "cc_proto_library", +) + +# begin:google_only +# package(default_applicable_licenses = ["//upb:license"]) +# end:google_only + +licenses(["notice"]) + +proto_library( + name = "test_model_proto", + srcs = [ + "child_model.proto", + "test_enum.proto", + "test_extension.proto", + "test_model.proto", + ], +) + +proto_library( + name = "no_package_proto", + srcs = [ + "no_package.proto", + ], +) + +proto_library( + name = "naming_conflict_proto", + srcs = [ + "naming_conflict.proto", + ], +) + +proto_library( + name = "no_package_enum_user_proto", + srcs = [ + "no_package_enum_user.proto", + ], + deps = [":no_package_proto"], +) + +upb_proto_library( + name = "test_model_upb_proto", + visibility = [ + "//protos:__pkg__", + ], + deps = [":test_model_proto"], +) + +upb_cc_proto_library( + name = "test_model_upb_cc_proto", + visibility = ["//protos:__pkg__"], + deps = [":test_model_proto"], +) + +upb_cc_proto_library( + name = "naming_conflict_upb_cc_proto", + visibility = [ + "//visibility:private", # Only private by automation, not intent. Owner may accept CLs adding visibility. See go/scheuklappen#explicit-private. + ], + deps = [":naming_conflict_proto"], +) + +upb_cc_proto_library( + name = "no_package_upb_cc_proto", + deps = [ + ":no_package_proto", + ], +) + +upb_cc_proto_library( + name = "no_package_enum_user_upb_cc_proto", + deps = [ + ":no_package_enum_user_proto", + ], +) + +cc_proto_library( + name = "test_model_cc_proto", + deps = [":test_model_proto"], +) + +# begin:google_only +# proto_library( +# name = "legacy_name_proto", +# srcs = [ +# "legacy-name.proto", +# ], +# ) +# +# upb_cc_proto_library( +# name = "legacy_name_test_proto", +# visibility = [ +# "//visibility:private", # Only private by automation, not intent. Owner may accept CLs adding visibility. See go/scheuklappen#explicit-private. +# ], +# deps = [":legacy_name_proto"], +# ) +# end:google_only + +cc_test( + name = "test_generated_cc_code", + srcs = ["test_generated.cc"], + copts = UPB_DEFAULT_CPPOPTS, + deps = [ + # begin:google_only +# ":legacy_name_test_proto", + # end:google_only + ":no_package_upb_cc_proto", + ":test_model_upb_cc_proto", + ":test_model_upb_proto", + ":naming_conflict_upb_cc_proto", + "@com_google_googletest//:gtest_main", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "//protos", + "//upb:mem", + "//protos:repeated_field", + ], +)
diff --git a/protos_generator/tests/child_model.proto b/protos_generator/tests/child_model.proto new file mode 100644 index 0000000..c7af6f1 --- /dev/null +++ b/protos_generator/tests/child_model.proto
@@ -0,0 +1,49 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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. + +syntax = "proto3"; + +package protos_generator.test; + +import public "protos_generator/tests/test_enum.proto"; + +message ChildModel1 { + optional bool child_b1 = 44; + optional string child_str1 = 56; +} + +message ChildModel3 { + string sub_key = 1; + bool bool1 = 2; + int32 i32 = 3; + optional string opt_str = 4; + optional bool opt_bool = 5; + optional int32 opt_i32 = 6; +}
diff --git a/protos_generator/tests/legacy-name.proto b/protos_generator/tests/legacy-name.proto new file mode 100644 index 0000000..0f256ad --- /dev/null +++ b/protos_generator/tests/legacy-name.proto
@@ -0,0 +1,40 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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. + +syntax = "proto3"; + +package protos_generator.test; + +// option java_multiple_files = true; + +enum LegacyEnum { + PHASE_DEFAULT = 0; + PHASE_BUSY = 1; +}
diff --git a/protos_generator/tests/naming_conflict.proto b/protos_generator/tests/naming_conflict.proto new file mode 100644 index 0000000..414f7c6 --- /dev/null +++ b/protos_generator/tests/naming_conflict.proto
@@ -0,0 +1,38 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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. + +syntax = "proto3"; + +package protos_generator.test; + +message HasChildCount { + optional HasChildCount has_child_count = 1; + optional int32 child_count = 2; +}
diff --git a/protos_generator/tests/no_package.proto b/protos_generator/tests/no_package.proto new file mode 100644 index 0000000..37d6df8 --- /dev/null +++ b/protos_generator/tests/no_package.proto
@@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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. + +syntax = "proto2"; + +// option java_multiple_files = true; + +enum EnumWithNoPackage { + CELSIUS = 1; + FAHRENHEIT = 2; +} + +message MessageWithEnumUpbTest { + enum EnumWithNoPackageInMessage { + UNKNOWN = 0; + AB_1 = 1; + CD_2 = 2; + EF_3 = 3; + GH_4 = 4; + } +}
diff --git a/protos_generator/tests/no_package_enum_user.proto b/protos_generator/tests/no_package_enum_user.proto new file mode 100644 index 0000000..ebf8c67 --- /dev/null +++ b/protos_generator/tests/no_package_enum_user.proto
@@ -0,0 +1,41 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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. + +syntax = "proto2"; + +package protos_generator.tests; + +import "protos_generator/tests/no_package.proto"; + +// option java_multiple_files = true; + +message MyMessage { + optional MessageWithEnumUpbTest my_type = 1; +}
diff --git a/protos_generator/tests/test_enum.proto b/protos_generator/tests/test_enum.proto new file mode 100644 index 0000000..ed41c3c --- /dev/null +++ b/protos_generator/tests/test_enum.proto
@@ -0,0 +1,40 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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. + +syntax = "proto3"; + +package protos_generator.test; + +enum TestEnum { + DEVICE_UNKNOWN = 0; + DEVICE_KEYBOARD = 1; + DEVICE_MOUSE = 2; + DEVICE_MONITOR = 3; +}
diff --git a/protos_generator/tests/test_extension.proto b/protos_generator/tests/test_extension.proto new file mode 100644 index 0000000..3a1a3c6 --- /dev/null +++ b/protos_generator/tests/test_extension.proto
@@ -0,0 +1,42 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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. + +syntax = "proto2"; + +package protos_generator.test.someotherpackage; + +import "protos_generator/tests/test_model.proto"; + +// Define extension that is extending proto outside this package with a type +// defined in different file. + +extend TestModel { + optional ThemeExtension styling = 13001; +}
diff --git a/protos_generator/tests/test_generated.cc b/protos_generator/tests/test_generated.cc new file mode 100644 index 0000000..1206b6a --- /dev/null +++ b/protos_generator/tests/test_generated.cc
@@ -0,0 +1,1158 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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 <limits> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "protos/protos.h" +#include "protos/repeated_field.h" +#include "protos/repeated_field_iterator.h" +#include "protos_generator/tests/child_model.upb.proto.h" +#include "protos_generator/tests/no_package.upb.proto.h" +#include "protos_generator/tests/test_model.upb.proto.h" +#include "upb/mem/arena.h" + +namespace { + +using ::protos_generator::test::protos::ChildModel1; +using ::protos_generator::test::protos::other_ext; +using ::protos_generator::test::protos::RED; +using ::protos_generator::test::protos::TestEnum; +using ::protos_generator::test::protos::TestModel; +using ::protos_generator::test::protos::TestModel_Category; +using ::protos_generator::test::protos::TestModel_Category_IMAGES; +using ::protos_generator::test::protos::TestModel_Category_NEWS; +using ::protos_generator::test::protos::TestModel_Category_VIDEO; +using ::protos_generator::test::protos::theme; +using ::protos_generator::test::protos::ThemeExtension; +using ::testing::ElementsAre; +using ::testing::HasSubstr; + +// C++17 port of C++20 `requires` +template <typename... T, typename F> +constexpr bool Requires(F) { + return std::is_invocable_v<F, T...>; +} + +TEST(CppGeneratedCode, Constructor) { TestModel test_model; } + +TEST(CppGeneratedCode, MessageEnum) { EXPECT_EQ(5, TestModel_Category_IMAGES); } + +TEST(CppGeneratedCode, ImportedEnum) { EXPECT_EQ(3, TestEnum::DEVICE_MONITOR); } + +TEST(CppGeneratedCode, Enum) { EXPECT_EQ(1, RED); } + +TEST(CppGeneratedCode, EnumNoPackage) { EXPECT_EQ(1, ::protos_CELSIUS); } + +TEST(CppGeneratedCode, MessageEnumType) { + TestModel_Category category1 = TestModel_Category_IMAGES; + TestModel::Category category2 = TestModel::IMAGES; + EXPECT_EQ(category1, category2); +} + +TEST(CppGeneratedCode, MessageEnumValue) { + EXPECT_EQ(TestModel_Category_IMAGES, TestModel::IMAGES); +} + +TEST(CppGeneratedCode, ArenaConstructor) { + ::protos::Arena arena; + auto testModel = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(false, testModel.has_b1()); +} + +TEST(CppGeneratedCode, Booleans) { + ::protos::Arena arena; + auto testModel = ::protos::CreateMessage<TestModel>(arena); + EXPECT_FALSE(testModel.b1()); + testModel.set_b1(true); + EXPECT_TRUE(testModel.b1()); + testModel.set_b1(false); + EXPECT_FALSE(testModel.b1()); + testModel.set_b1(true); + EXPECT_TRUE(testModel.b1()); + testModel.clear_b1(); + EXPECT_FALSE(testModel.has_b1()); +} + +TEST(CppGeneratedCode, ScalarInt32) { + ::protos::Arena arena; + auto testModel = ::protos::CreateMessage<TestModel>(arena); + // Test int32 defaults. + EXPECT_EQ(testModel.value(), 0); + EXPECT_FALSE(testModel.has_value()); + // Floating point defautls. + EXPECT_EQ(std::numeric_limits<float>::infinity(), + testModel.float_value_with_default()); + EXPECT_EQ(-std::numeric_limits<double>::infinity(), + testModel.double_value_with_default()); + + // Set value. + testModel.set_value(5); + EXPECT_TRUE(testModel.has_value()); + EXPECT_EQ(testModel.value(), 5); + // Change value. + testModel.set_value(10); + EXPECT_TRUE(testModel.has_value()); + EXPECT_EQ(testModel.value(), 10); + // Clear value. + testModel.clear_value(); + EXPECT_FALSE(testModel.has_value()); + EXPECT_EQ(testModel.value(), 0); +} + +const char kTestStr1[] = "abcdefg"; +const char kTestStr2[] = "just another test string"; + +TEST(CppGeneratedCode, Strings) { + TestModel testModel; + testModel.set_str1(kTestStr1); + testModel.set_str2(kTestStr2); + EXPECT_EQ(testModel.str1(), kTestStr1); + EXPECT_EQ(testModel.str2(), kTestStr2); + EXPECT_TRUE(testModel.has_str1()); + EXPECT_TRUE(testModel.has_str2()); + + testModel.clear_str1(); + EXPECT_FALSE(testModel.has_str1()); + EXPECT_TRUE(testModel.has_str2()); +} + +TEST(CppGeneratedCode, ScalarUInt32) { + ::protos::Arena arena; + auto testModel = ::protos::CreateMessage<TestModel>(arena); + // Test defaults. + EXPECT_EQ(testModel.optional_uint32(), 0); + EXPECT_FALSE(testModel.has_optional_uint32()); + // Set value. + testModel.set_optional_uint32(0xA0001000); + EXPECT_TRUE(testModel.has_optional_uint32()); + EXPECT_EQ(testModel.optional_uint32(), 0xA0001000); + // Change value. + testModel.set_optional_uint32(0x70002000); + EXPECT_TRUE(testModel.has_optional_uint32()); + EXPECT_EQ(testModel.optional_uint32(), 0x70002000); + // Clear value. + testModel.clear_optional_uint32(); + EXPECT_FALSE(testModel.has_optional_uint32()); + EXPECT_EQ(testModel.optional_uint32(), 0); +} + +TEST(CppGeneratedCode, ScalarInt64) { + ::protos::Arena arena; + auto testModel = ::protos::CreateMessage<TestModel>(arena); + // Test defaults. + EXPECT_EQ(testModel.optional_int64(), 0); + EXPECT_FALSE(testModel.has_optional_int64()); + // Set value. + testModel.set_optional_int64(0xFF00CCDDA0001000); + EXPECT_TRUE(testModel.has_optional_int64()); + EXPECT_EQ(testModel.optional_int64(), 0xFF00CCDDA0001000); + // Change value. + testModel.set_optional_int64(0xFF00CCDD70002000); + EXPECT_TRUE(testModel.has_optional_int64()); + EXPECT_EQ(testModel.optional_int64(), 0xFF00CCDD70002000); + // Clear value. + testModel.clear_optional_int64(); + EXPECT_FALSE(testModel.has_optional_int64()); + EXPECT_EQ(testModel.optional_int64(), 0); + // Set after clear. + testModel.set_optional_int64(0xFF00CCDDA0001000); + EXPECT_TRUE(testModel.has_optional_int64()); + EXPECT_EQ(testModel.optional_int64(), 0xFF00CCDDA0001000); +} + +TEST(CppGeneratedCode, ScalarFloat) { + ::protos::Arena arena; + auto testModel = ::protos::CreateMessage<TestModel>(arena); + // Test defaults. + EXPECT_EQ(testModel.optional_float(), 0.0f); + EXPECT_FALSE(testModel.has_optional_float()); + EXPECT_EQ(std::numeric_limits<float>::infinity(), + testModel.float_value_with_default()); + EXPECT_EQ(-std::numeric_limits<double>::infinity(), + testModel.double_value_with_default()); + // Set value. + testModel.set_optional_float(3.14159265f); + EXPECT_TRUE(testModel.has_optional_float()); + EXPECT_NEAR(testModel.optional_float(), 3.14159265f, 1e-9f); + // Change value. + testModel.set_optional_float(-2.0f); + EXPECT_TRUE(testModel.has_optional_float()); + EXPECT_NEAR(testModel.optional_float(), -2, 1e-9f); + // Clear value. + testModel.clear_optional_float(); + EXPECT_FALSE(testModel.has_optional_float()); + EXPECT_EQ(testModel.optional_float(), 0.0f); + // Set after clear. + testModel.set_optional_float(3.14159265f); + EXPECT_TRUE(testModel.has_optional_float()); + EXPECT_NEAR(testModel.optional_float(), 3.14159265f, 1e-9f); +} + +TEST(CppGeneratedCode, ScalarDouble) { + ::protos::Arena arena; + auto testModel = ::protos::CreateMessage<TestModel>(arena); + // Test defaults. + EXPECT_EQ(testModel.optional_double(), 0.0); + EXPECT_FALSE(testModel.has_optional_double()); + // Set value. + testModel.set_optional_double(3.141592653589793); + EXPECT_TRUE(testModel.has_optional_double()); + EXPECT_NEAR(testModel.optional_double(), 3.141592653589793, 1e-16f); + // Change value. + testModel.set_optional_double(-1.0); + EXPECT_TRUE(testModel.has_optional_double()); + EXPECT_NEAR(testModel.optional_double(), -1.0, 1e-16f); + // Clear value. + testModel.clear_optional_double(); + EXPECT_FALSE(testModel.has_optional_double()); + EXPECT_EQ(testModel.optional_double(), 0.0f); + // Set after clear. + testModel.set_optional_double(3.141592653589793); + EXPECT_TRUE(testModel.has_optional_double()); + EXPECT_NEAR(testModel.optional_double(), 3.141592653589793, 1e-16f); +} + +TEST(CppGeneratedCode, Enums) { + ::protos::Arena arena; + auto testModel = ::protos::CreateMessage<TestModel>(arena); + + // Check enum default value. + EXPECT_EQ(TestModel_Category_IMAGES, 5); + + // Test defaults. + EXPECT_FALSE(testModel.has_category()); + EXPECT_EQ(testModel.category(), TestModel_Category_IMAGES); + // Set value. + testModel.set_category(TestModel_Category_NEWS); + EXPECT_TRUE(testModel.has_category()); + EXPECT_EQ(testModel.category(), TestModel_Category_NEWS); + // Change value. + testModel.set_category(TestModel_Category_VIDEO); + EXPECT_TRUE(testModel.has_category()); + EXPECT_EQ(testModel.category(), TestModel_Category_VIDEO); + // Clear value. + testModel.clear_category(); + EXPECT_FALSE(testModel.has_category()); + EXPECT_EQ(testModel.category(), TestModel_Category_IMAGES); + // Set after clear. + testModel.set_category(TestModel_Category_VIDEO); + EXPECT_TRUE(testModel.has_category()); + EXPECT_EQ(testModel.category(), TestModel_Category_VIDEO); +} + +TEST(CppGeneratedCode, FieldWithDefaultValue) { + ::protos::Arena arena; + auto testModel = ::protos::CreateMessage<TestModel>(arena); + + EXPECT_FALSE(testModel.has_int_value_with_default()); + EXPECT_EQ(testModel.int_value_with_default(), 65); + testModel.set_int_value_with_default(10); + EXPECT_EQ(testModel.int_value_with_default(), 10); + + EXPECT_FALSE(testModel.has_string_value_with_default()); + EXPECT_EQ(testModel.string_value_with_default(), "hello"); + testModel.set_string_value_with_default("new string"); + EXPECT_EQ(testModel.string_value_with_default(), "new string"); +} + +TEST(CppGeneratedCode, OneOfFields) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + + EXPECT_FALSE(test_model.has_oneof_member1()); + EXPECT_FALSE(test_model.has_oneof_member2()); + EXPECT_EQ(TestModel::CHILD_ONEOF1_NOT_SET, test_model.child_oneof1_case()); + + test_model.set_oneof_member1("one of string"); + EXPECT_TRUE(test_model.has_oneof_member1()); + EXPECT_FALSE(test_model.has_oneof_member2()); + EXPECT_EQ(test_model.oneof_member1(), "one of string"); + EXPECT_EQ(TestModel::kOneofMember1, test_model.child_oneof1_case()); + + test_model.set_oneof_member2(true); + EXPECT_FALSE(test_model.has_oneof_member1()); + EXPECT_TRUE(test_model.has_oneof_member2()); + EXPECT_EQ(test_model.oneof_member2(), true); + EXPECT_EQ(TestModel::kOneofMember2, test_model.child_oneof1_case()); + + test_model.clear_oneof_member2(); + EXPECT_FALSE(test_model.has_oneof_member1()); + EXPECT_FALSE(test_model.has_oneof_member2()); + EXPECT_EQ(test_model.oneof_member1(), ""); + EXPECT_EQ(test_model.oneof_member2(), false); + EXPECT_EQ(TestModel::CHILD_ONEOF1_NOT_SET, test_model.child_oneof1_case()); +} + +TEST(CppGeneratedCode, Messages) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(false, test_model.has_child_model_1()); + auto child_model = test_model.child_model_1(); + EXPECT_EQ(false, child_model->has_child_b1()); + EXPECT_EQ(false, child_model->child_b1()); + auto mutable_child = test_model.mutable_child_model_1(); + mutable_child->set_child_b1(true); + EXPECT_EQ(true, mutable_child->has_child_b1()); + EXPECT_EQ(true, mutable_child->child_b1()); + // The View should not change due to mutation since it + // is default_instance. + EXPECT_EQ(false, child_model->has_child_b1()); + // Readonly View should now show change. + child_model = test_model.child_model_1(); + EXPECT_EQ(true, child_model->has_child_b1()); + EXPECT_EQ(true, child_model->child_b1()); + // Clear message field. + EXPECT_EQ(true, test_model.has_child_model_1()); + test_model.clear_child_model_1(); + EXPECT_EQ(false, test_model.has_child_model_1()); +} + +TEST(CppGeneratedCode, NestedMessages) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + auto nested_child = test_model.nested_child_1(); + EXPECT_EQ(0, nested_child->nested_child_name().size()); + auto mutable_nested_child = test_model.mutable_nested_child_1(); + EXPECT_EQ(false, mutable_nested_child->has_nested_child_name()); + mutable_nested_child->set_nested_child_name(kTestStr1); + EXPECT_EQ(true, mutable_nested_child->has_nested_child_name()); +} + +TEST(CppGeneratedCode, RepeatedMessages) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.child_models_size()); + // Should be able to clear repeated field when empty. + test_model.mutable_child_models()->clear(); + EXPECT_EQ(0, test_model.child_models_size()); + // Add 2 children. + auto new_child = test_model.add_child_models(); + EXPECT_EQ(true, new_child.ok()); + new_child.value()->set_child_str1(kTestStr1); + new_child = test_model.add_child_models(); + EXPECT_EQ(true, new_child.ok()); + new_child.value()->set_child_str1(kTestStr2); + EXPECT_EQ(2, test_model.child_models_size()); + // Mutable access. + auto mutable_first = test_model.mutable_child_models(0); + EXPECT_EQ(mutable_first->child_str1(), kTestStr1); + mutable_first->set_child_str1("change1"); + auto mutable_second = test_model.mutable_child_models(1); + EXPECT_EQ(mutable_second->child_str1(), kTestStr2); + mutable_second->set_child_str1("change2"); + // Check mutations using views. + auto view_first = test_model.child_models(0); + EXPECT_EQ(view_first->child_str1(), "change1"); + auto view_second = test_model.child_models(1); + EXPECT_EQ(view_second->child_str1(), "change2"); +} + +TEST(CppGeneratedCode, RepeatedScalar) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.value_array_size()); + // Should be able to clear repeated field when empty. + test_model.mutable_value_array()->clear(); + EXPECT_EQ(0, test_model.value_array_size()); + // Add 2 children. + EXPECT_EQ(true, test_model.add_value_array(5)); + EXPECT_EQ(true, test_model.add_value_array(6)); + EXPECT_EQ(2, test_model.value_array_size()); + EXPECT_EQ(5, test_model.value_array(0)); + EXPECT_EQ(6, test_model.value_array(1)); + EXPECT_EQ(true, test_model.resize_value_array(3)); + EXPECT_EQ(3, test_model.value_array_size()); + test_model.set_value_array(2, 7); + EXPECT_EQ(5, test_model.value_array(0)); + EXPECT_EQ(6, test_model.value_array(1)); + EXPECT_EQ(7, test_model.value_array(2)); +} + +TEST(CppGeneratedCode, RepeatedFieldClear) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + test_model.mutable_value_array()->push_back(5); + test_model.mutable_value_array()->push_back(16); + test_model.mutable_value_array()->push_back(27); + ASSERT_EQ(test_model.mutable_value_array()->size(), 3); + test_model.mutable_value_array()->clear(); + EXPECT_EQ(test_model.mutable_value_array()->size(), 0); +} + +TEST(CppGeneratedCode, RepeatedFieldProxyForScalars) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.value_array().size()); + EXPECT_EQ(0, test_model.mutable_value_array()->size()); + + test_model.mutable_value_array()->push_back(5); + test_model.mutable_value_array()->push_back(16); + test_model.mutable_value_array()->push_back(27); + + ASSERT_EQ(test_model.mutable_value_array()->size(), 3); + EXPECT_EQ((*test_model.mutable_value_array())[0], 5); + EXPECT_EQ((*test_model.mutable_value_array())[1], 16); + EXPECT_EQ((*test_model.mutable_value_array())[2], 27); + + const auto value_array = test_model.value_array(); + ASSERT_EQ(value_array.size(), 3); + EXPECT_EQ(value_array[0], 5); + EXPECT_EQ(value_array[1], 16); + EXPECT_EQ(value_array[2], 27); + + EXPECT_THAT(value_array, ElementsAre(5, 16, 27)); + + EXPECT_THAT(std::vector(value_array.begin(), value_array.end()), + ElementsAre(5, 16, 27)); + EXPECT_THAT(std::vector(value_array.cbegin(), value_array.cend()), + ElementsAre(5, 16, 27)); + EXPECT_THAT(std::vector(value_array.rbegin(), value_array.rend()), + ElementsAre(27, 16, 5)); + EXPECT_THAT(std::vector(value_array.crbegin(), value_array.crend()), + ElementsAre(27, 16, 5)); +} + +TEST(CppGeneratedCode, RepeatedScalarIterator) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + test_model.mutable_value_array()->push_back(5); + test_model.mutable_value_array()->push_back(16); + test_model.mutable_value_array()->push_back(27); + int sum = 0; + // Access by value. + const ::protos::RepeatedField<int32_t>::CProxy rep1 = + test_model.value_array(); + for (auto i : rep1) { + sum += i; + } + EXPECT_EQ(sum, 5 + 16 + 27); + // Access by const reference. + sum = 0; + for (const int& i : *test_model.mutable_value_array()) { + sum += i; + } + EXPECT_EQ(sum, 5 + 16 + 27); + // Access by forwarding reference. + sum = 0; + for (auto&& i : *test_model.mutable_value_array()) { + sum += i; + } + EXPECT_EQ(sum, 5 + 16 + 27); + // Test iterator operators. + auto begin = test_model.value_array().begin(); + auto end = test_model.value_array().end(); + sum = 0; + for (auto it = begin; it != end; ++it) { + sum += *it; + } + EXPECT_EQ(sum, 5 + 16 + 27); + auto it = begin; + ++it; + EXPECT_TRUE(begin < it); + EXPECT_TRUE(begin <= it); + it = end; + EXPECT_TRUE(it == end); + EXPECT_TRUE(it > begin); + EXPECT_TRUE(it >= begin); + EXPECT_TRUE(it != begin); + // difference type + it = end; + --it; + --it; + EXPECT_EQ(end - it, 2); + it = begin; + EXPECT_EQ(it[0], 5); + EXPECT_EQ(it[1], 16); + EXPECT_EQ(it[2], 27); + // ValueProxy. + sum = 0; + for (::protos::RepeatedField<int32_t>::ValueCProxy c : + test_model.value_array()) { + sum += c; + } + EXPECT_EQ(sum, 5 + 16 + 27); + sum = 0; + for (::protos::RepeatedField<int32_t>::ValueProxy c : + *test_model.mutable_value_array()) { + sum += c; + } + EXPECT_EQ(sum, 5 + 16 + 27); +} + +TEST(CppGeneratedCode, RepeatedFieldProxyForStrings) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.repeated_string().size()); + EXPECT_EQ(0, test_model.mutable_repeated_string()->size()); + + test_model.mutable_repeated_string()->push_back("a"); + test_model.mutable_repeated_string()->push_back("b"); + test_model.mutable_repeated_string()->push_back("c"); + + ASSERT_EQ(test_model.repeated_string().size(), 3); + EXPECT_EQ(test_model.repeated_string()[0], "a"); + EXPECT_EQ(test_model.repeated_string()[1], "b"); + EXPECT_EQ(test_model.repeated_string()[2], "c"); + + EXPECT_THAT(test_model.repeated_string(), ElementsAre("a", "b", "c")); + EXPECT_THAT(*test_model.mutable_repeated_string(), + ElementsAre("a", "b", "c")); + + ASSERT_EQ(test_model.mutable_repeated_string()->size(), 3); + EXPECT_EQ((*test_model.mutable_repeated_string())[0], "a"); + EXPECT_EQ((*test_model.mutable_repeated_string())[1], "b"); + EXPECT_EQ((*test_model.mutable_repeated_string())[2], "c"); + + // The const accessor can't be used to modify the element + EXPECT_FALSE((std::is_assignable<decltype(test_model.repeated_string()[1]), + absl::string_view>::value)); + // But the mutable one is fine. + (*test_model.mutable_repeated_string())[1] = "other"; + EXPECT_THAT(test_model.repeated_string(), ElementsAre("a", "other", "c")); + + test_model.mutable_repeated_string()->clear(); + EXPECT_EQ(test_model.mutable_repeated_string()->size(), 0); +} + +TEST(CppGeneratedCode, RepeatedFieldProxyForMessages) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.child_models().size()); + ChildModel1 child1; + child1.set_child_str1(kTestStr1); + test_model.mutable_child_models()->push_back(child1); + ChildModel1 child2; + child2.set_child_str1(kTestStr2); + test_model.mutable_child_models()->push_back(std::move(child2)); + + int i = 0; + for (auto child : test_model.child_models()) { + EXPECT_FALSE(Requires<decltype(child)>( + [](auto&& x) -> decltype(x.set_child_str1("")) {})); + if (i++ == 0) { + EXPECT_EQ(child.child_str1(), kTestStr1); + } else { + EXPECT_EQ(child.child_str1(), kTestStr2); + } + } + + i = 0; + for (auto child : *test_model.mutable_child_models()) { + if (i++ == 0) { + EXPECT_EQ(child.child_str1(), kTestStr1); + } else { + EXPECT_EQ(child.child_str1(), kTestStr2); + } + } + + using testing::_; + EXPECT_THAT(test_model.child_models(), ElementsAre(_, _)); + + EXPECT_EQ(test_model.child_models().size(), 2); + EXPECT_EQ(test_model.child_models()[0].child_str1(), kTestStr1); + EXPECT_EQ(test_model.child_models()[1].child_str1(), kTestStr2); + EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), kTestStr1); + EXPECT_EQ((*test_model.mutable_child_models())[1].child_str1(), kTestStr2); + (*test_model.mutable_child_models())[0].set_child_str1("change1"); + EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), "change1"); + test_model.mutable_child_models()->clear(); + EXPECT_EQ(test_model.mutable_child_models()->size(), 0); +} + +TEST(CppGeneratedCode, RepeatedFieldProxyForMessagesIndexOperator) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.child_models().size()); + ChildModel1 child1; + child1.set_child_str1(kTestStr1); + test_model.mutable_child_models()->push_back(child1); + ChildModel1 child2; + + child2.set_child_str1(kTestStr2); + test_model.mutable_child_models()->push_back(std::move(child2)); + ASSERT_EQ(test_model.child_models().size(), 2); + + // test_model.child_models()[0].set_child_str1("change1"); + (*test_model.mutable_child_models())[0].set_child_str1("change1"); + EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), "change1"); +} + +TEST(CppGeneratedCode, RepeatedStrings) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.repeated_string_size()); + // Should be able to clear repeated field when empty. + test_model.mutable_repeated_string()->clear(); + EXPECT_EQ(0, test_model.repeated_string_size()); + // Add 2 children. + EXPECT_EQ(true, test_model.add_repeated_string("Hello")); + EXPECT_EQ(true, test_model.add_repeated_string("World")); + EXPECT_EQ(2, test_model.repeated_string_size()); + EXPECT_EQ("Hello", test_model.repeated_string(0)); + EXPECT_EQ("World", test_model.repeated_string(1)); + EXPECT_EQ(true, test_model.resize_repeated_string(3)); + EXPECT_EQ(3, test_model.repeated_string_size()); + test_model.set_repeated_string(2, "Test"); + EXPECT_EQ("Hello", test_model.repeated_string(0)); + EXPECT_EQ("World", test_model.repeated_string(1)); + EXPECT_EQ("Test", test_model.repeated_string(2)); +} + +TEST(CppGeneratedCode, MessageMapInt32KeyMessageValue) { + const int key_test_value = 3; + ::protos::Arena arena; + ::protos::Arena child_arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.child_map_size()); + test_model.clear_child_map(); + EXPECT_EQ(0, test_model.child_map_size()); + auto child_model1 = ::protos::CreateMessage<ChildModel1>(child_arena); + child_model1.set_child_str1("abc"); + test_model.set_child_map(key_test_value, child_model1); + auto map_result = test_model.get_child_map(key_test_value); + EXPECT_EQ(true, map_result.ok()); + EXPECT_EQ("abc", map_result.value()->child_str1()); + // Now mutate original child model to verify that value semantics are + // preserved. + child_model1.set_child_str1("abc V2"); + EXPECT_EQ("abc", map_result.value()->child_str1()); + test_model.delete_child_map(key_test_value); + auto map_result_after_delete = test_model.get_child_map(key_test_value); + EXPECT_EQ(false, map_result_after_delete.ok()); +} + +TEST(CppGeneratedCode, MessageMapStringKeyAndStringValue) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.str_to_str_map_size()); + test_model.clear_str_to_str_map(); + EXPECT_EQ(0, test_model.str_to_str_map_size()); + test_model.set_str_to_str_map("first", "abc"); + test_model.set_str_to_str_map("second", "def"); + auto result = test_model.get_str_to_str_map("second"); + EXPECT_EQ(true, result.ok()); + EXPECT_EQ("def", result.value()); + test_model.delete_str_to_str_map("first"); + auto result_after_delete = test_model.get_str_to_str_map("first"); + EXPECT_EQ(false, result_after_delete.ok()); +} + +TEST(CppGeneratedCode, MessageMapStringKeyAndInt32Value) { + ::protos::Arena arena; + auto test_model = ::protos::CreateMessage<TestModel>(arena); + EXPECT_EQ(0, test_model.str_to_int_map_size()); + test_model.clear_str_to_int_map(); + EXPECT_EQ(0, test_model.str_to_int_map_size()); + test_model.set_str_to_int_map("first", 10); + EXPECT_EQ(1, test_model.str_to_int_map_size()); + test_model.set_str_to_int_map("second", 20); + EXPECT_EQ(2, test_model.str_to_int_map_size()); + auto result = test_model.get_str_to_int_map("second"); + EXPECT_EQ(true, result.ok()); + EXPECT_EQ(20, result.value()); + test_model.delete_str_to_int_map("first"); + auto result_after_delete = test_model.get_str_to_int_map("first"); + EXPECT_EQ(false, result_after_delete.ok()); +} + +TEST(CppGeneratedCode, HasExtension) { + TestModel model; + EXPECT_EQ(false, ::protos::HasExtension(&model, theme)); +} + +TEST(CppGeneratedCode, HasExtensionPtr) { + TestModel model; + EXPECT_EQ(false, ::protos::HasExtension(model.recursive_child(), theme)); +} + +TEST(CppGeneratedCode, ClearExtensionWithEmptyExtension) { + TestModel model; + EXPECT_EQ(false, ::protos::HasExtension(&model, theme)); + ::protos::ClearExtension(&model, theme); + EXPECT_EQ(false, ::protos::HasExtension(&model, theme)); +} + +TEST(CppGeneratedCode, ClearExtensionWithEmptyExtensionPtr) { + TestModel model; + ::protos::Ptr<TestModel> recursive_child = model.mutable_recursive_child(); + ::protos::ClearExtension(recursive_child, theme); + EXPECT_EQ(false, ::protos::HasExtension(recursive_child, theme)); +} + +TEST(CppGeneratedCode, SetExtension) { + TestModel model; + void* prior_message; + { + // Use a nested scope to make sure the arenas are fused correctly. + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + prior_message = ::protos::internal::GetInternalMsg(&extension1); + EXPECT_EQ(false, ::protos::HasExtension(&model, theme)); + EXPECT_EQ( + true, + ::protos::SetExtension(&model, theme, std::move(extension1)).ok()); + } + EXPECT_EQ(true, ::protos::HasExtension(&model, theme)); + auto ext = ::protos::GetExtension(&model, theme); + EXPECT_TRUE(ext.ok()); + EXPECT_EQ(::protos::internal::GetInternalMsg(*ext), prior_message); +} + +TEST(CppGeneratedCode, SetExtensionFusingFailureShouldCopy) { + // Use an initial block to disallow fusing. + char initial_block[1000]; + protos::Arena arena(initial_block, sizeof(initial_block)); + + protos::Ptr<TestModel> model = protos::CreateMessage<TestModel>(arena); + + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + ASSERT_FALSE( + upb_Arena_Fuse(arena.ptr(), ::protos::internal::GetArena(&extension1))); + EXPECT_FALSE(::protos::HasExtension(model, theme)); + auto status = ::protos::SetExtension(model, theme, std::move(extension1)); + EXPECT_TRUE(status.ok()); + EXPECT_TRUE(::protos::HasExtension(model, theme)); + EXPECT_TRUE(::protos::GetExtension(model, theme).ok()); +} + +TEST(CppGeneratedCode, SetExtensionShouldClone) { + TestModel model; + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + EXPECT_EQ(false, ::protos::HasExtension(&model, theme)); + EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok()); + extension1.set_ext_name("Goodbye"); + EXPECT_EQ(true, ::protos::HasExtension(&model, theme)); + auto ext = ::protos::GetExtension(&model, theme); + EXPECT_TRUE(ext.ok()); + EXPECT_EQ((*ext)->ext_name(), "Hello World"); +} + +TEST(CppGeneratedCode, SetExtensionShouldCloneConst) { + TestModel model; + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + EXPECT_EQ(false, ::protos::HasExtension(&model, theme)); + EXPECT_EQ( + true, + ::protos::SetExtension(&model, theme, std::as_const(extension1)).ok()); + extension1.set_ext_name("Goodbye"); + EXPECT_EQ(true, ::protos::HasExtension(&model, theme)); + auto ext = ::protos::GetExtension(&model, theme); + EXPECT_TRUE(ext.ok()); + EXPECT_EQ((*ext)->ext_name(), "Hello World"); +} + +TEST(CppGeneratedCode, SetExtensionOnMutableChild) { + TestModel model; + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + EXPECT_EQ(false, + ::protos::HasExtension(model.mutable_recursive_child(), theme)); + EXPECT_EQ(true, ::protos::SetExtension(model.mutable_recursive_child(), theme, + extension1) + .ok()); + EXPECT_EQ(true, + ::protos::HasExtension(model.mutable_recursive_child(), theme)); +} + +TEST(CppGeneratedCode, GetExtension) { + TestModel model; + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + EXPECT_EQ(false, ::protos::HasExtension(&model, theme)); + EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok()); + EXPECT_EQ("Hello World", + ::protos::GetExtension(&model, theme).value()->ext_name()); +} + +TEST(CppGeneratedCode, GetExtensionOnMutableChild) { + TestModel model; + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + ::protos::Ptr<TestModel> mutable_recursive_child = + model.mutable_recursive_child(); + EXPECT_EQ(false, ::protos::HasExtension(mutable_recursive_child, theme)); + EXPECT_EQ( + true, + ::protos::SetExtension(mutable_recursive_child, theme, extension1).ok()); + EXPECT_EQ("Hello World", + ::protos::GetExtension(mutable_recursive_child, theme) + .value() + ->ext_name()); +} + +TEST(CppGeneratedCode, GetExtensionOnImmutableChild) { + TestModel model; + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + ::protos::Ptr<TestModel> mutable_recursive_child = + model.mutable_recursive_child(); + EXPECT_EQ(false, ::protos::HasExtension(mutable_recursive_child, theme)); + EXPECT_EQ( + true, + ::protos::SetExtension(mutable_recursive_child, theme, extension1).ok()); + ::protos::Ptr<const TestModel> recursive_child = model.recursive_child(); + EXPECT_EQ("Hello World", + ::protos::GetExtension(recursive_child, theme).value()->ext_name()); +} + +TEST(CppGeneratedCode, SerializeUsingArena) { + TestModel model; + model.set_str1("Hello World"); + ::upb::Arena arena; + absl::StatusOr<absl::string_view> bytes = ::protos::Serialize(&model, arena); + EXPECT_EQ(true, bytes.ok()); + TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value(); + EXPECT_EQ("Hello World", parsed_model.str1()); +} + +TEST(CppGeneratedCode, SerializeProxyUsingArena) { + ::upb::Arena message_arena; + TestModel::Proxy model_proxy = + ::protos::CreateMessage<TestModel>(message_arena); + model_proxy.set_str1("Hello World"); + ::upb::Arena arena; + absl::StatusOr<absl::string_view> bytes = + ::protos::Serialize(&model_proxy, arena); + EXPECT_EQ(true, bytes.ok()); + TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value(); + EXPECT_EQ("Hello World", parsed_model.str1()); +} + +TEST(CppGeneratedCode, SerializeNestedMessageUsingArena) { + TestModel model; + model.mutable_recursive_child()->set_str1("Hello World"); + ::upb::Arena arena; + ::protos::Ptr<const TestModel> child = model.recursive_child(); + absl::StatusOr<absl::string_view> bytes = ::protos::Serialize(child, arena); + EXPECT_EQ(true, bytes.ok()); + TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value(); + EXPECT_EQ("Hello World", parsed_model.str1()); +} + +TEST(CppGeneratedCode, Parse) { + TestModel model; + model.set_str1("Test123"); + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok()); + ::upb::Arena arena; + auto bytes = ::protos::Serialize(&model, arena); + EXPECT_EQ(true, bytes.ok()); + TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value(); + EXPECT_EQ("Test123", parsed_model.str1()); + EXPECT_EQ(true, ::protos::GetExtension(&parsed_model, theme).ok()); +} + +TEST(CppGeneratedCode, ParseIntoPtrToModel) { + TestModel model; + model.set_str1("Test123"); + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok()); + ::upb::Arena arena; + auto bytes = ::protos::Serialize(&model, arena); + EXPECT_EQ(true, bytes.ok()); + ::protos::Ptr<TestModel> parsed_model = + ::protos::CreateMessage<TestModel>(arena); + EXPECT_TRUE(::protos::Parse(parsed_model, bytes.value())); + EXPECT_EQ("Test123", parsed_model->str1()); + // Should return an extension even if we don't pass ExtensionRegistry + // by promoting unknown. + EXPECT_EQ(true, ::protos::GetExtension(parsed_model, theme).ok()); +} + +TEST(CppGeneratedCode, ParseWithExtensionRegistry) { + TestModel model; + model.set_str1("Test123"); + ThemeExtension extension1; + extension1.set_ext_name("Hello World"); + EXPECT_EQ(true, ::protos::SetExtension(&model, theme, extension1).ok()); + EXPECT_EQ(true, ::protos::SetExtension( + &model, ThemeExtension::theme_extension, extension1) + .ok()); + ::upb::Arena arena; + auto bytes = ::protos::Serialize(&model, arena); + EXPECT_EQ(true, bytes.ok()); + ::protos::ExtensionRegistry extensions( + {&theme, &other_ext, &ThemeExtension::theme_extension}, arena); + TestModel parsed_model = + ::protos::Parse<TestModel>(bytes.value(), extensions).value(); + EXPECT_EQ("Test123", parsed_model.str1()); + EXPECT_EQ(true, ::protos::GetExtension(&parsed_model, theme).ok()); + EXPECT_EQ(true, ::protos::GetExtension(&parsed_model, + ThemeExtension::theme_extension) + .ok()); + EXPECT_EQ("Hello World", ::protos::GetExtension( + &parsed_model, ThemeExtension::theme_extension) + .value() + ->ext_name()); +} + +TEST(CppGeneratedCode, NameCollisions) { + TestModel model; + model.set_template_("test"); + EXPECT_EQ("test", model.template_()); + model.set_arena__("test"); + EXPECT_EQ("test", model.arena__()); +} + +TEST(CppGeneratedCode, SharedPointer) { + std::shared_ptr<TestModel> model = std::make_shared<TestModel>(); + ::upb::Arena arena; + auto bytes = protos::Serialize(model.get(), arena); + EXPECT_TRUE(protos::Parse(model.get(), bytes.value())); +} + +TEST(CppGeneratedCode, UniquePointer) { + auto model = std::make_unique<TestModel>(); + ::upb::Arena arena; + auto bytes = protos::Serialize(model.get(), arena); + EXPECT_TRUE(protos::Parse(model.get(), bytes.value())); +} + +TEST(CppGeneratedCode, Assignment) { + TestModel model; + model.set_category(5); + model.mutable_child_model_1()->set_child_str1("text in child"); + TestModel model2 = model; + EXPECT_EQ(5, model2.category()); + EXPECT_EQ(model2.child_model_1()->child_str1(), "text in child"); +} + +TEST(CppGeneratedCode, PtrAssignment) { + TestModel model; + model.mutable_child_model_1()->set_child_str1("text in child"); + ChildModel1 child_from_const_ptr = *model.child_model_1(); + EXPECT_EQ(child_from_const_ptr.child_str1(), "text in child"); + ChildModel1 child_from_ptr = *model.mutable_child_model_1(); + EXPECT_EQ(child_from_ptr.child_str1(), "text in child"); +} + +TEST(CppGeneratedCode, CopyConstructor) { + TestModel model; + model.set_category(6); + TestModel model2(model); + EXPECT_EQ(6, model2.category()); +} + +TEST(CppGeneratedCode, PtrConstructor) { + TestModel model; + model.mutable_child_model_1()->set_child_str1("text in child"); + ChildModel1 child_from_ptr(*model.mutable_child_model_1()); + EXPECT_EQ(child_from_ptr.child_str1(), "text in child"); + ChildModel1 child_from_const_ptr(*model.child_model_1()); + EXPECT_EQ(child_from_const_ptr.child_str1(), "text in child"); +} + +TEST(CppGeneratedCode, MutableToProxy) { + TestModel model; + ::protos::Ptr<ChildModel1> child = model.mutable_child_model_1(); + (void)child; +} + +TEST(CppGeneratedCode, ProxyToCProxy) { + TestModel model; + ::protos::Ptr<ChildModel1> child = model.mutable_child_model_1(); + ::protos::Ptr<const ChildModel1> child2 = child; + (void)child2; +} + +TEST(CppGeneratedCode, MutableAccessorsAreHiddenInCProxy) { + TestModel model; + ::protos::Ptr<TestModel> proxy = &model; + ::protos::Ptr<const TestModel> cproxy = proxy; + + const auto test_const_accessors = [](auto p) { + // We don't want to run it, just check it compiles. + if (false) { + (void)p->has_str1(); + (void)p->str1(); + (void)p->has_value(); + (void)p->value(); + (void)p->has_oneof_member1(); + (void)p->oneof_member1(); + (void)p->value_array(); + (void)p->value_array_size(); + (void)p->value_array(1); + (void)p->has_nested_child_1(); + (void)p->nested_child_1(); + (void)p->child_models(); + (void)p->child_models_size(); + (void)p->child_models(1); + (void)p->child_map_size(); + (void)p->get_child_map(1); + } + }; + + test_const_accessors(proxy); + test_const_accessors(cproxy); + + const auto test_mutable_accessors = [](auto p, bool expected) { + const auto r = [&](auto l) { return Requires<decltype(p)>(l) == expected; }; + EXPECT_TRUE(r([](auto p) -> decltype(p->set_str1("")) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->clear_str1()) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->set_value(1)) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->clear_value()) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->set_oneof_member1("")) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->clear_oneof_member1()) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->mutable_nested_child_1()) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->clear_nested_child_1()) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->add_value_array(1)) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->mutable_value_array()) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->resize_value_array(1)) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->set_value_array(1, 1)) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->add_child_models()) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->mutable_child_models(1)) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->clear_child_map()) {})); + EXPECT_TRUE(r([](auto p) -> decltype(p->delete_child_map(1)) {})); + EXPECT_TRUE(r( + [](auto p) -> decltype(p->set_child_map(1, *p->get_child_map(1))) {})); + }; + test_mutable_accessors(proxy, true); + test_mutable_accessors(cproxy, false); +} + +bool ProxyToCProxyMethod(::protos::Ptr<const ChildModel1> child) { + return child->child_str1() == "text in child"; +} + +TEST(CppGeneratedCode, PassProxyToCProxy) { + TestModel model; + model.mutable_child_model_1()->set_child_str1("text in child"); + EXPECT_TRUE(ProxyToCProxyMethod(model.mutable_child_model_1())); +} + +TEST(CppGeneratedCode, PtrImplicitConversion) { + TestModel model; + model.set_int64(5); + ::protos::Ptr<TestModel> model_ptr = &model; + EXPECT_EQ(model_ptr->int64(), 5); +} + +TEST(CppGeneratedCode, ClearSubMessage) { + // Fill model. + TestModel model; + model.set_int64(5); + auto new_child = model.mutable_child_model_1(); + new_child->set_child_str1("text in child"); + ThemeExtension extension1; + extension1.set_ext_name("name in extension"); + EXPECT_TRUE(::protos::SetExtension(&model, theme, extension1).ok()); + EXPECT_TRUE(model.mutable_child_model_1()->has_child_str1()); + // Clear using Ptr<T> + ::protos::ClearMessage(model.mutable_child_model_1()); + EXPECT_FALSE(model.mutable_child_model_1()->has_child_str1()); +} + +TEST(CppGeneratedCode, ClearMessage) { + // Fill model. + TestModel model; + model.set_int64(5); + model.set_str2("Hello"); + auto new_child = model.add_child_models(); + ASSERT_TRUE(new_child.ok()); + new_child.value()->set_child_str1("text in child"); + ThemeExtension extension1; + extension1.set_ext_name("name in extension"); + EXPECT_TRUE(::protos::SetExtension(&model, theme, extension1).ok()); + // Clear using T* + ::protos::ClearMessage(&model); + // Verify that scalars, repeated fields and extensions are cleared. + EXPECT_FALSE(model.has_int64()); + EXPECT_FALSE(model.has_str2()); + EXPECT_TRUE(model.child_models().empty()); + EXPECT_FALSE(::protos::HasExtension(&model, theme)); +} + +TEST(CppGeneratedCode, DeepCopy) { + // Fill model. + TestModel model; + model.set_int64(5); + model.set_str2("Hello"); + auto new_child = model.add_child_models(); + ASSERT_TRUE(new_child.ok()); + new_child.value()->set_child_str1("text in child"); + ThemeExtension extension1; + extension1.set_ext_name("name in extension"); + EXPECT_TRUE(::protos::SetExtension(&model, theme, extension1).ok()); + TestModel target; + target.set_b1(true); + ::protos::DeepCopy(&model, &target); + EXPECT_FALSE(target.b1()) << "Target was not cleared before copying content "; + EXPECT_EQ(target.str2(), "Hello"); + EXPECT_TRUE(::protos::HasExtension(&target, theme)); +} + +TEST(CppGeneratedCode, HasExtensionAndRegistry) { + // Fill model. + TestModel source; + source.set_int64(5); + source.set_str2("Hello"); + auto new_child = source.add_child_models(); + ASSERT_TRUE(new_child.ok()); + new_child.value()->set_child_str1("text in child"); + ThemeExtension extension1; + extension1.set_ext_name("name in extension"); + ASSERT_TRUE(::protos::SetExtension(&source, theme, extension1).ok()); + + // Now that we have a source model with extension data, serialize. + ::protos::Arena arena; + std::string data = std::string(::protos::Serialize(&source, arena).value()); + + // Test with ExtensionRegistry + ::protos::ExtensionRegistry extensions({&theme}, arena); + TestModel parsed_model = ::protos::Parse<TestModel>(data, extensions).value(); + EXPECT_TRUE(::protos::HasExtension(&parsed_model, theme)); +} + +// TODO : Add BUILD rule to test failures below. +#ifdef TEST_CLEAR_MESSAGE_FAILURE +TEST(CppGeneratedCode, ClearConstMessageShouldFail) { + // Fill model. + TestModel model; + model.set_int64(5); + model.set_str2("Hello"); + // Only mutable_ can be cleared not Ptr<const T>. + ::protos::ClearMessage(model.child_model_1()); +} +#endif + +} // namespace
diff --git a/protos_generator/tests/test_model.proto b/protos_generator/tests/test_model.proto new file mode 100644 index 0000000..e133624 --- /dev/null +++ b/protos_generator/tests/test_model.proto
@@ -0,0 +1,184 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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 THE COPYRIGHT +// OWNER OR CONTRIBUTORS 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. + +syntax = "proto2"; + +package protos_generator.test; + +import "protos_generator/tests/child_model.proto"; + +message TestModelContainer { + repeated TestModel models = 1; + optional ChildModel3 proto_3_child = 2; +} + +message TestModel { + optional int32 value = 1; + repeated int32 value_array = 2; // _UPB_MODE_ARRAY + repeated int32 value_packed_array = 3 + [packed = true]; // _UPB_MODE_ARRAY | _UPB_MODE_IS_PACKED + repeated int32 value_deprec = 4 [deprecated = true]; + optional string str1 = 115; + optional bool b1 = 9; + optional bool b2 = 10; + optional string str2 = 50; + optional string str3 = 11; + optional float optional_float = 14; + optional double optional_double = 15; + optional int64 optional_int64 = 16; + optional uint32 optional_uint32 = 17; + optional uint64 optional_uint64 = 18; + optional sint32 optional_sint32 = 19; + optional sint64 optional_sint64 = 20; + optional fixed32 optional_fixed32 = 21; + optional fixed64 optional_fixed64 = 22; + optional sfixed32 optional_sfixed32 = 23; + optional sfixed64 optional_sfixed64 = 24; + repeated int64 repeated_int64 = 25; + repeated uint64 repeated_uint64 = 26; + repeated fixed64 repeated_fixed64 = 27; + repeated sfixed64 repeated_sfixed64 = 28; + repeated bool repeated_bool = 29; + repeated string repeated_string = 35; + optional bytes optional_bytes = 36; + message NestedChild { + optional string nested_child_name = 211; + } + optional NestedChild nested_child_1 = 212; + optional ChildModel1 child_model_1 = 222; + repeated ChildModel1 child_models = 223; + optional ChildModel1 bar = 224; + oneof child_oneof1 { + string oneof_member1 = 98; + bool oneof_member2 = 99; + } + optional int32 int_value_with_default = 31 + [default = 65]; // Not supported yet + optional string string_value_with_default = 32 + [default = "hello"]; // Not supported yet + optional float float_value_with_default = 33 [default = inf]; + optional float double_value_with_default = 34 [default = -inf]; + + map<int32, ChildModel1> child_map = 225; + optional TestModel recursive_child = 226; + map<string, ChildModel1> child_str_map = 227; + map<string, int32> str_to_int_map = 228; + map<string, string> str_to_str_map = 229; + + extend TestAnnotation { + optional OtherExtension in_message_ext = 15000; + } + + enum Category { + IMAGES = 5; + NEWS = 6; + VIDEO = 7; + RADIO = 8 [deprecated = true]; + } + optional Category category = 37; + + // keyword collisions (double, template, ...) + oneof type { + string string = 230; + int64 int64 = 231; + double double = 232; + } + optional string template = 233; + optional string msg = 234; + optional string arena = 235; + + // Tests publicly imported enum. + optional TestEnum imported_enum = 238; + + optional string phase = 239; + optional bool clear_phase = 240; + + optional string doc_id = 241; + optional bool set_doc_id = 242; + + extensions 10000 to max; +} + +// Old version with fewer fields to test backward/forward compatibility. +message TestModelContainerV1 { + repeated TestModelV1 models = 1; +} + +message TestModelV1 { + optional int32 value = 1; + repeated int32 value2 = 2; + repeated int32 value3 = 3 [packed = true]; + repeated int32 value4 = 4 [deprecated = true]; + optional bool b1 = 9; + optional bool b2 = 10; + optional string str2 = 50; +} + +enum PrimaryColors { + RED = 1; + GREEN = 2; + BLUE = 3; +} + +// TestModel extension. +message ThemeExtension { + extend TestModel { + optional ThemeExtension theme_extension = 12003; + } + optional string ext_name = 1; + optional bool ext_bool = 2; +} + +extend TestModel { + optional ThemeExtension theme = 12001; +} + +message OtherExtension { + optional string ext2_name = 1; +} + +extend TestModel { + optional OtherExtension other_ext = 12002; +} + +message TestAnnotation { + extensions 10000 to max; +} + +message TestMessageHasEnum { + optional EnumDeclaredAfterMessage enum_declared_after_message = 1; +} + +enum EnumDeclaredAfterMessage { + ZERO = 0; + ONE = 1; + TWO = 2; + THREE = 3; +}