C++ Code generator PiperOrigin-RevId: 486158566
diff --git a/protos_generator/BUILD b/protos_generator/BUILD new file mode 100644 index 0000000..394668e --- /dev/null +++ b/protos_generator/BUILD
@@ -0,0 +1,85 @@ +# Copyright (c) 2009-2021, Google LLC +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Google LLC nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +load( + "//bazel:build_defs.bzl", + "UPB_DEFAULT_CPPOPTS", +) + +licenses(["notice"]) + +cc_binary( + name = "protoc-gen-upb-protos", + srcs = [ + "protoc-gen-upb-protos.cc", + ], + copts = UPB_DEFAULT_CPPOPTS, + visibility = ["//visibility:public"], + deps = [ + ":generator", + "//:mini_table", + "//upbc:file_layout", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/strings", + "@com_google_protobuf//:protobuf", + "@com_google_protobuf//src/google/protobuf/compiler:code_generator", + ], +) + +cc_library( + name = "generator", + srcs = [ + "gen_accessors.cc", + "gen_enums.cc", + "gen_extensions.cc", + "gen_messages.cc", + "gen_utils.cc", + "output.cc", + ], + hdrs = [ + "gen_accessors.h", + "gen_enums.h", + "gen_extensions.h", + "gen_messages.h", + "gen_utils.h", + "output.h", + ], + visibility = ["//visibility:private"], + deps = [ + "//upbc:common", + "//upbc:file_layout", + "//upbc:keywords", + "@com_google_absl//absl/base:log_severity", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/log", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/strings", + "@com_google_protobuf//:protobuf", + "@com_google_protobuf//src/google/protobuf/compiler:code_generator", + ], +)
diff --git a/protos_generator/gen_accessors.cc b/protos_generator/gen_accessors.cc new file mode 100644 index 0000000..740daab --- /dev/null +++ b/protos_generator/gen_accessors.cc
@@ -0,0 +1,722 @@ +// Copyright (c) 2009-2021, Google LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google LLC nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "protos_generator/gen_accessors.h" + +#include "google/protobuf/descriptor.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" +#include "protos_generator/gen_utils.h" +#include "protos_generator/output.h" +#include "upbc/file_layout.h" +#include "upbc/keywords.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +using FileLayout = ::upbc::FileLayout; +using NameToFieldDescriptorMap = + absl::flat_hash_map<absl::string_view, const protobuf::FieldDescriptor*>; + +void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const FileLayout& layout, Output& output); +void WriteFieldAccessorClear(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const FileLayout& layout, Output& output); +void WriteMapFieldAccessors(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + Output& output); + +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); +void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + absl::string_view class_name, Output& output); +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); +void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message, + const protobuf::FieldDescriptor* field, + const 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) { + FileLayout layout(desc->file()); + + // Generate const methods. + OutputIndenter i(output); + + auto field_names = CreateFieldNameMap(desc); + + for (auto field : ::upbc::FieldNumberOrder(desc)) { + std::string resolved_field_name = ResolveFieldName(field, field_names); + absl::string_view upbc_name = field->name(); + WriteFieldAccessorHazzer(desc, field, resolved_field_name, layout, output); + WriteFieldAccessorClear(desc, field, resolved_field_name, layout, output); + + if (field->is_map()) { + WriteMapFieldAccessors(desc, field, resolved_field_name, output); + } else if (desc->options().map_entry()) { + // TODO(b/237399867) Implement map entry + } else if (field->is_repeated()) { + output( + R"cc( + inline size_t $1_size() const { + size_t len; + $0_$2(msg_, &len); + return len; + } + + inline void clear_$1() { $0_clear_$2(msg_); } + )cc", + MessageName(desc), resolved_field_name, upbc_name); + + if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + output( + R"cc( + $1 $2(size_t index) const; + absl::StatusOr<$0> add_$2(); + $0 mutable_$3(size_t index) const; + )cc", + MessagePtrConstType(field, /* const */ false), + MessagePtrConstType(field, /* const */ true), resolved_field_name, + upbc_name); + } else { + output( + R"cc( + $0 $1(size_t index) const; + 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 { + // 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_$3(); + )cc", + MessagePtrConstType(field, /* const */ false), + MessagePtrConstType(field, /* const */ true), + resolved_field_name, 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), + upbc_name); + } + } + } +} + +void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const FileLayout& layout, Output& output) { + // Generate hazzer (if any). + if (layout.HasHasbit(field) || field->real_containing_oneof()) { + absl::string_view upbc_name = field->name(); + // Has presence. + output("inline bool has_$0() const { return $1_has_$2(msg_); }\n", + resolved_field_name, MessageName(desc), upbc_name); + } +} + +void WriteFieldAccessorClear(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_name, + const FileLayout& layout, Output& output) { + if (layout.HasHasbit(field) || field->real_containing_oneof()) { + absl::string_view upbc_name = field->name(); + output("void clear_$0() { $2_clear_$1(msg_); }\n", resolved_field_name, + upbc_name, MessageName(desc)); + } +} + +void WriteMapFieldAccessors(const protobuf::Descriptor* desc, + const protobuf::FieldDescriptor* field, + const absl::string_view resolved_field_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(); + 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), 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, + const FileLayout& layout, 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); + + // Generate const methods. + OutputIndenter i(output); + for (auto field : ::upbc::FieldNumberOrder(desc)) { + std::string resolved_field_name = ResolveFieldName(field, field_names); + if (field->is_map()) { + WriteMapAccessorDefinitions(desc, field, resolved_field_name, class_name, + output); + } else if (desc->options().map_entry()) { + // TODO(b/237399867) 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 { + absl::string_view upbc_name = field->name(); + // 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), 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, 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_))); + } + )cc", + class_name, MessagePtrConstType(field, /* is_const */ true), + resolved_field_name, MessageName(desc), + MessageBaseType(field, /* maybe_const */ false), 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), upbc_name, + arena_expression); + } + } + } + output("\n"); + output("} // namespace internal\n\n"); +} + +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(0 <= index && index < len); + return ::protos::internal::CreateMessage<$4>((upb_Message*)*(ptr + index)); + } + )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(0 <= index && 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); +} + +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(0 <= index && 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(0 <= index && index < len); + *(ptr + index) = ::protos::UpbStrFromStringView(val, arena_); + } + )cc", + class_name, CppConstType(field), resolved_field_name, + MessageName(message), upbc_name); +} + +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(0 <= index && 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(0 <= index && index < len); + *(ptr + index) = val; + } + )cc", + class_name, CppConstType(field), resolved_field_name, + MessageName(message), upbc_name); +} + +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) { + $6return $4_$8_set(msg_, $7, ($5*)value->msg(), 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); + output( + R"cc( + bool $0::set_$1($2 key, $3 value) { + $6return $4_$8_set(msg_, $7, ($5*)value->msg(), 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); + 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); + } + 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) { + FileLayout layout(desc->file()); + 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 (auto field : ::upbc::FieldNumberOrder(desc)) { + std::string resolved_field_name = ResolveFieldName(field, field_names); + // Generate hazzer (if any). + if (layout.HasHasbit(field) || field->real_containing_oneof()) { + // Has presence. + output("using $0Access::has_$1;\n", class_name, resolved_field_name); + } + // Generate clear. + if (layout.HasHasbit(field) || field->real_containing_oneof()) { + output("using $0Access::clear_$1;\n", class_name, resolved_field_name); + } + if (field->is_map()) { + output( + R"cc( + using $0Access::$1_size; + using $0Access::clear_$1; + using $0Access::delete_$1; + using $0Access::get_$1; + using $0Access::set_$1; + )cc", + class_name, resolved_field_name); + } else if (desc->options().map_entry()) { + // TODO(b/237399867) Implement map entry + } else if (field->is_repeated()) { + if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + output( + R"cc( + using $0Access::$1; + using $0Access::$1_size; + using $0Access::mutable_$1; + )cc", + class_name, resolved_field_name); + if (!read_only) { + output( + R"cc( + using $0Access::add_$1; + using $0Access::clear_$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::clear_$1; + using $0Access::resize_$1; + using $0Access::set_$1; + )cc", + class_name, resolved_field_name); + } + } + } 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); + } + } + } + } + output("using $0Access::msg;\n", class_name); +} + +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_", + }); + + // 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[] = { + "clear_", + "delete_", + "add_", + "resize_", + }; + + 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()) { + 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..90da017 --- /dev/null +++ b/protos_generator/gen_accessors.h
@@ -0,0 +1,46 @@ +// Copyright (c) 2009-2021, Google LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google LLC nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#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" +#include "upbc/file_layout.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc, + Output& output); +void WriteAccessorsInSource(const protobuf::Descriptor* desc, + const ::upbc::FileLayout& layout, Output& output); +void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc, + MessageClassType handle_type, 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..0197cd1 --- /dev/null +++ b/protos_generator/gen_enums.cc
@@ -0,0 +1,126 @@ +// Copyright (c) 2009-2021, Google LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google LLC nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "protos_generator/gen_enums.h" + +#include "google/protobuf/descriptor.pb.h" +#include "google/protobuf/descriptor.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("protos_", ToCIdent(enum_descriptor->name())); + } + return ToCIdent(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("protos_", 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)); + if (value->options().deprecated()) { + output(" ABSL_DEPRECATED(\"Proto enum $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..8e7d6ea --- /dev/null +++ b/protos_generator/gen_enums.h
@@ -0,0 +1,44 @@ +// Copyright (c) 2009-2021, Google LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google LLC nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#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); +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..94a0a12 --- /dev/null +++ b/protos_generator/gen_extensions.cc
@@ -0,0 +1,110 @@ +// Copyright (c) 2009-2021, Google LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google LLC nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "protos_generator/gen_extensions.h" + +#include "absl/strings/str_cat.h" +#include "protos_generator/gen_utils.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 ::protos::internal::ExtensionIdentifier<$0, $1> $2; + )cc", + ContainingTypeName(ext), CppTypeParameterName(ext), ext->name()); + } else { + output( + R"cc( + extern ::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( + ::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( + ::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..7313acb --- /dev/null +++ b/protos_generator/gen_extensions.h
@@ -0,0 +1,49 @@ +// Copyright (c) 2009-2021, Google LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google LLC nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#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..7433ba5 --- /dev/null +++ b/protos_generator/gen_messages.cc
@@ -0,0 +1,380 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "protos_generator/gen_messages.h" + +#include "google/protobuf/descriptor.pb.h" +#include "google/protobuf/descriptor.h" +#include "protos_generator/gen_accessors.h" +#include "protos_generator/gen_extensions.h" +#include "protos_generator/gen_utils.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, + 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); + +// 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, + 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;", ClassName(descriptor)); + output("namespace internal {\n"); + WriteModelAccessDeclaration(descriptor, output); + output("\n"); + WriteInternalForwardDeclarationsInHeader(descriptor, output); + output("\n"); + output("} // namespace internal\n"); + WriteModelPublicDeclaration(descriptor, file_exts, output); + output("namespace internal {\n"); + WriteModelProxyDeclaration(descriptor, output); + WriteModelCProxyDeclaration(descriptor, output); + output("} // namespace internal\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) {} // NOLINT + $0Access(const $1* msg, upb_Arena* arena) + : msg_(const_cast<$1*>(msg)), arena_(arena) {} // NOLINT + void* GetInternalArena() const { return arena_; } + )cc", + ClassName(descriptor), MessageName(descriptor)); + WriteFieldAccessorsInHeader(descriptor, output); + output.Indent(); + output( + R"cc( + private: + void* msg() const { return msg_; } + + friend class $2; + friend class $0Proxy; + friend class $0CProxy; + friend void* ::protos::internal::GetInternalMsg<$2>(const $2& message); + friend void* ::protos::internal::GetInternalMsg<$2>( + const ::protos::Ptr<$2>& message); + $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, + 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& m) = delete; + $0& operator=(const $0& m) = delete; + $0($0&& m) + : Access(m.msg_, m.arena_), + owned_arena_(std::move(m.owned_arena_)) {} + $0& operator=($0&& m) { + msg_ = m.msg_; + arena_ = m.arena_; + m.msg_ = nullptr; + m.arena_ = nullptr; + owned_arena_ = std::move(m.owned_arena_); + return *this; + } + )cc", + ClassName(descriptor)); + + WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessage, output); + WriteDefaultInstanceHeader(descriptor, output); + WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output); + output.Indent(); + output.Indent(); + 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( + R"cc( + static const upb_MiniTable* minitable(); + using $0Access::GetInternalArena; + + private: + $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 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>(const $0& message); + friend upb_Arena* ::protos::internal::GetArena<$0>( + const ::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 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: + $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 class $0CProxy; + friend class $0Access; + friend class ::protos::Ptr<$0>; + friend class ::protos::Ptr<const $0>; + friend upb_Arena* ::protos::internal::GetArena<$2>(const $2& message); + friend upb_Arena* ::protos::internal::GetArena<$2>( + const ::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_, nullptr) {} + using $0Access::GetInternalArena; + )cc", + ClassName(descriptor), MessageName(descriptor)); + + WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy, + output); + + output.Indent(1); + output( + R"cc( + private: + $0CProxy(void* msg) : internal::$0Access(($1*)msg, nullptr){}; + friend $0::CProxy(::protos::internal::CreateMessage<$0>(upb_Message* msg)); + friend class ::protos::Ptr<$0>; + friend class ::protos::Ptr<const $0>; + 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_); + } + )cc", + ClassName(descriptor), MessageName(descriptor)); + // Minitable + OutputIndenter i(output); + output( + R"cc( + const upb_MiniTable* $0::minitable() { return &$1; } + )cc", + ClassName(descriptor), ::upbc::MessageInit(descriptor)); + } + + ::upbc::FileLayout layout(descriptor->file()); + WriteAccessorsInSource(descriptor, layout, output); + + if (!message_is_map_entry) { + output( + R"cc( + struct $0DefaultTypeInternal { + $1* msg; + }; + $0DefaultTypeInternal _$0_default_instance_ = + $0DefaultTypeInternal{$1_new(upb_Arena_New())}; + )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); + } + )cc", + ClassName(descriptor)); + } +} + +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); + } + } +} + +} // namespace protos_generator
diff --git a/protos_generator/gen_messages.h b/protos_generator/gen_messages.h new file mode 100644 index 0000000..0b8aa95 --- /dev/null +++ b/protos_generator/gen_messages.h
@@ -0,0 +1,47 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#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, + 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_utils.cc b/protos_generator/gen_utils.cc new file mode 100644 index 0000000..bd8a2b3 --- /dev/null +++ b/protos_generator/gen_utils.cc
@@ -0,0 +1,203 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "protos_generator/gen_utils.h" + +#include <string> + +#include "absl/strings/str_cat.h" +// begin:google_only +// #include "absl/log/check.h" +// #include "absl/strings/str_replace.h" +// end:google_only +#include "absl/strings/str_split.h" +#include "upbc/keywords.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +// begin:github_only +#ifndef DCHECK +#ifndef NDEBUG +#define DCHECK(condition) if(!(condition)) LOG(FATAL); +#else +#define DCHECK(condition) if(!(true || condition)) LOG(FATAL); +#endif +#endif +// end:github_only + +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 ClassName(const protobuf::Descriptor* descriptor) { + const protobuf::Descriptor* parent = descriptor->containing_type(); + std::string res; + 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 UpbCFilename(const google::protobuf::FileDescriptor* file) { + return StripExtension(file->name()) + ".upb.h"; +} + +std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file) { + return StripExtension(file->name()) + ".upb.fwd.h"; +} + +std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file) { + return StripExtension(file->name()) + ".upb.proto.h"; +} + +std::string NamespaceFromPackageName(absl::string_view package_name) { + return absl::StrCat(absl::StrReplaceAll(package_name, {{".", "::"}}), + "::protos"); +} + +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 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: + LOG(FATAL) << "Unexpected type: " << field->cpp_type(); + } +} + +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) { + 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) { + 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) { + 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) { + 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/gen_utils.h b/protos_generator/gen_utils.h new file mode 100644 index 0000000..02ab8b5 --- /dev/null +++ b/protos_generator/gen_utils.h
@@ -0,0 +1,81 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_PROTOS_GENERATOR_GEN_UTILS_H_ +#define UPB_PROTOS_GENERATOR_GEN_UTILS_H_ + +#include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/plugin.h" +#include "google/protobuf/descriptor.pb.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/wire_format.h" +#include "absl/container/flat_hash_map.h" +#include "protos_generator/output.h" + +namespace protos_generator { + +namespace protobuf = ::google::protobuf; + +enum class MessageClassType { + kMessage, + kMessageCProxy, + kMessageProxy, + kMessageAccess, +}; + +std::string ClassName(const protobuf::Descriptor* descriptor); +std::string QualifiedClassName(const protobuf::Descriptor* descriptor); +std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor); + +inline bool IsMapEntryMessage(const protobuf::Descriptor* descriptor) { + return descriptor->options().map_entry(); +} +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_GEN_UTILS_H_
diff --git a/protos_generator/output.cc b/protos_generator/output.cc new file mode 100644 index 0000000..e5a9b79 --- /dev/null +++ b/protos_generator/output.cc
@@ -0,0 +1,85 @@ +// Copyright (c) 2009-2021, Google LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google LLC nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "protos_generator/output.h" + +#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..fa6c683 --- /dev/null +++ b/protos_generator/output.h
@@ -0,0 +1,172 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_PROTOS_GENERATOR_OUTPUT_H +#define UPB_PROTOS_GENERATOR_OUTPUT_H + +#include <vector> + +#include "google/protobuf/io/zero_copy_stream.h" +#include "google/protobuf/descriptor.h" +#include "absl/base/log_severity.h" +#include "absl/log/log.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/substitute.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) { + 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..f9522f7 --- /dev/null +++ b/protos_generator/protoc-gen-upb-protos.cc
@@ -0,0 +1,306 @@ +// Copyright (c) 2009-2021, Google LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google LLC nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <memory> + +#include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/plugin.h" +#include "google/protobuf/descriptor.pb.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/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; +using FileLayout = ::upbc::FileLayout; + +void WriteSource(const FileLayout& layout, Output& output, + bool fasttable_enabled); +void WriteHeader(const FileLayout& layout, Output& output); +void WriteForwardingHeader(const FileLayout& layout, 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, + const std::vector<const protobuf::Descriptor*>& file_messages, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + 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; + } + } + + FileLayout layout(file); + + // Write model.upb.fwd.h + Output forwarding_header_output( + context->Open(ForwardingHeaderFilename(file))); + WriteForwardingHeader(layout, forwarding_header_output); + // Write model.upb.proto.h + Output header_output(context->Open(CppHeaderFilename(file))); + WriteHeader(layout, header_output); + // Write model.upb.proto.cc + Output cc_output(context->Open(CppSourceFilename(file))); + WriteSource(layout, 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 FileLayout& layout, Output& output) { + const protobuf::FileDescriptor* file = layout.descriptor(); + EmitFileWarning(file, output); + output( + R"cc( +#ifndef $0_UPB_FWD_H_ +#define $0_UPB_FWD_H_ + )cc", + ToPreproc(file->name())); + output("\n"); + const std::vector<const protobuf::Descriptor*> this_file_messages = + ::upbc::SortedMessages(file); + WriteTypedefForwardingHeader(file, this_file_messages, output); + output("#endif /* $0_UPB_FWD_H_ */\n", ToPreproc(file->name())); +} + +void WriteHeader(const FileLayout& layout, Output& output) { + const protobuf::FileDescriptor* file = layout.descriptor(); + 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 "upb/upb.hpp" + +#include "absl/strings/string_view.h" +#include "absl/status/statusor.h" +#include "upb/msg_internal.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 = + ::upbc::SortedMessages(file); + const std::vector<const protobuf::FieldDescriptor*> this_file_exts = + ::upbc::SortedExtensions(file); + + if (!this_file_messages.empty()) { + output("\n"); + } + + WriteHeaderMessageForwardDecls(file, this_file_messages, this_file_exts, + output); + WriteStartNamespace(file, output); + + std::vector<const protobuf::EnumDescriptor*> this_file_enums = + ::upbc::SortedEnums(file); + + // Write Class and Enums. + WriteEnumDeclarations(this_file_enums, output); + output("\n"); + + for (auto message : this_file_messages) { + WriteMessageClassDeclarations(message, this_file_exts, 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 FileLayout& layout, Output& output, + bool fasttable_enabled) { + const protobuf::FileDescriptor* file = layout.descriptor(); + EmitFileWarning(file, output); + + output( + R"cc( +#include <stddef.h> +#include "absl/strings/string_view.h" +#include "upb/msg_internal.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 = + ::upbc::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 = + ::upbc::SortedExtensions(file); + const std::vector<const protobuf::Descriptor*> this_file_messages = + ::upbc::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, + const std::vector<const protobuf::Descriptor*>& file_messages, + const std::vector<const protobuf::FieldDescriptor*>& file_exts, + Output& output) { + // Import forward-declaration of types defined in this file. + output("#include \"$0\"\n", UpbCFilename(file)); + output("#include \"$0\"\n", ForwardingHeaderFilename(file)); + // Forward-declare types not in this file, but used as submessages. + // Order by full name for consistent ordering. + std::map<std::string, const protobuf::Descriptor*> forward_messages; + + for (auto* message : file_messages) { + for (int i = 0; i < message->field_count(); i++) { + const protobuf::FieldDescriptor* field = message->field(i); + if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE && + field->file() != field->message_type()->file()) { + forward_messages[field->message_type()->full_name()] = + field->message_type(); + } + } + } + for (auto* ext : file_exts) { + if (ext->file() != ext->containing_type()->file()) { + forward_messages[ext->containing_type()->full_name()] = + ext->containing_type(); + if (ext->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { + forward_messages[ext->message_type()->full_name()] = + ext->message_type(); + } + } + } + std::map<std::string, const protobuf::FileDescriptor*> files_to_import; + for (const auto& pair : forward_messages) { + files_to_import[ForwardingHeaderFilename(pair.second->file())] = file; + } + for (const auto& pair : files_to_import) { + output("#include \"$0\"\n", UpbCFilename(pair.second)); + output("#include \"$0\"\n", pair.first); + } + 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..4151ba7 --- /dev/null +++ b/protos_generator/tests/BUILD
@@ -0,0 +1,118 @@ +# Copyright (c) 2009-2021, Google LLC +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Google LLC nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +load( + "//bazel:build_defs.bzl", + "UPB_DEFAULT_CPPOPTS", +) +load( + "//protos/bazel:upb_cc_proto_library.bzl", + "upb_cc_proto_library", +) +load( + "//bazel:upb_proto_library.bzl", + "upb_proto_library", +) +load( + "@rules_cc//cc:defs.bzl", + "cc_proto_library", +) + +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", + ], +) + +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__", + "//protos_generator/nc_tests:__pkg__", + ], + deps = [":test_model_proto"], +) + +upb_cc_proto_library( + name = "no_package_upb_cc_proto", + deps = [":no_package_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 = ["//protos_generator/nc_tests:__pkg__"], +# 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", + "@com_google_googletest//:gtest_main", + "//:upb", + ], +)
diff --git a/protos_generator/tests/child_model.proto b/protos_generator/tests/child_model.proto new file mode 100644 index 0000000..05d0c41 --- /dev/null +++ b/protos_generator/tests/child_model.proto
@@ -0,0 +1,19 @@ +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..8685219 --- /dev/null +++ b/protos_generator/tests/legacy-name.proto
@@ -0,0 +1,10 @@ +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/no_package.proto b/protos_generator/tests/no_package.proto new file mode 100644 index 0000000..a52376b --- /dev/null +++ b/protos_generator/tests/no_package.proto
@@ -0,0 +1,8 @@ +syntax = "proto2"; + +// option java_multiple_files = true; + +enum EnumWithNoPackage { + CELSIUS = 1; + FAHRENHEIT = 2; +}
diff --git a/protos_generator/tests/test_enum.proto b/protos_generator/tests/test_enum.proto new file mode 100644 index 0000000..c5354cc --- /dev/null +++ b/protos_generator/tests/test_enum.proto
@@ -0,0 +1,10 @@ +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..e720773 --- /dev/null +++ b/protos_generator/tests/test_extension.proto
@@ -0,0 +1,12 @@ +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..f9fcea5 --- /dev/null +++ b/protos_generator/tests/test_generated.cc
@@ -0,0 +1,582 @@ +// Copyright (c) 2009-2021, Google LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google LLC nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <limits> + +#include "gtest/gtest.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/string_view.h" + +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_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; + +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, 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()); + + 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"); + + 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); + + 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); +} + +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_model_2_size()); + // Should be able to clear repeated field when empty. + test_model.clear_child_model_2(); + EXPECT_EQ(0, test_model.child_model_2_size()); + // Add 2 children. + auto new_child = test_model.add_child_model_2(); + EXPECT_EQ(true, new_child.ok()); + new_child.value()->set_child_str1(kTestStr1); + new_child = test_model.add_child_model_2(); + EXPECT_EQ(true, new_child.ok()); + new_child.value()->set_child_str1(kTestStr2); + EXPECT_EQ(2, test_model.child_model_2_size()); + // Mutable access. + auto mutable_first = test_model.mutable_child_model_2(0); + EXPECT_EQ(mutable_first->child_str1(), kTestStr1); + mutable_first->set_child_str1("change1"); + auto mutable_second = test_model.mutable_child_model_2(1); + EXPECT_EQ(mutable_second->child_str1(), kTestStr2); + mutable_second->set_child_str1("change2"); + // Check mutations using views. + auto view_first = test_model.child_model_2(0); + EXPECT_EQ(view_first->child_str1(), "change1"); + auto view_second = test_model.child_model_2(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.clear_value_array(); + 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, 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.clear_repeated_string(); + 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; + 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>(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()); + 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); + test_model.set_str_to_int_map("second", 20); + 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; + 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(true, ::protos::HasExtension(model, theme)); +} + +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, 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()); + // Should not return an extension since we did not pass ExtensionRegistry. + EXPECT_EQ(false, ::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()); + ::upb::Arena arena; + auto bytes = ::protos::Serialize(model, arena); + EXPECT_EQ(true, bytes.ok()); + ::protos::ExtensionRegistry extensions({&theme, &other_ext}, 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()); +} + +TEST(CppGeneratedCode, NameCollisions) { + TestModel model; + model.set_template_("test"); + EXPECT_EQ("test", model.template_()); + model.set_arena__("test"); + EXPECT_EQ("test", model.arena__()); +}
diff --git a/protos_generator/tests/test_model.proto b/protos_generator/tests/test_model.proto new file mode 100644 index 0000000..9109153 --- /dev/null +++ b/protos_generator/tests/test_model.proto
@@ -0,0 +1,149 @@ +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_model_2 = 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; + + // TODO(243705098) accessor name collision with field name + // optional repeated string phase = 236; + // optional bool clear_phase = 237; + + 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 { + 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; +}