Protocol Buffers - Google's data interchange format (grpc依赖)
https://developers.google.com/protocol-buffers/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
540 lines
19 KiB
540 lines
19 KiB
// Protocol Buffers - Google's data interchange format |
|
// Copyright 2023 Google LLC. All rights reserved. |
|
// |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file or at |
|
// https://developers.google.com/open-source/licenses/bsd |
|
|
|
#include "google/protobuf/compiler/hpb/gen_messages.h" |
|
|
|
#include <cstddef> |
|
#include <string> |
|
#include <vector> |
|
|
|
#include "google/protobuf/descriptor.pb.h" |
|
#include "absl/strings/ascii.h" |
|
#include "absl/strings/str_cat.h" |
|
#include "absl/strings/string_view.h" |
|
#include "google/protobuf/descriptor.h" |
|
#include "google/protobuf/compiler/hpb/gen_accessors.h" |
|
#include "google/protobuf/compiler/hpb/gen_enums.h" |
|
#include "google/protobuf/compiler/hpb/gen_extensions.h" |
|
#include "google/protobuf/compiler/hpb/gen_utils.h" |
|
#include "google/protobuf/compiler/hpb/names.h" |
|
#include "google/protobuf/compiler/hpb/output.h" |
|
#include "upb_generator/common.h" |
|
#include "upb_generator/file_layout.h" |
|
|
|
namespace google::protobuf::hpb_generator { |
|
|
|
namespace protobuf = ::proto2; |
|
|
|
void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor, |
|
Output& output); |
|
void WriteModelPublicDeclaration( |
|
const protobuf::Descriptor* descriptor, |
|
const std::vector<const protobuf::FieldDescriptor*>& file_exts, |
|
const std::vector<const protobuf::EnumDescriptor*>& file_enums, |
|
Output& output); |
|
void WriteExtensionIdentifiersInClassHeader( |
|
const protobuf::Descriptor* message, |
|
const std::vector<const protobuf::FieldDescriptor*>& file_exts, |
|
Output& output); |
|
void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor, |
|
Output& output); |
|
void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor, |
|
Output& output); |
|
void WriteInternalForwardDeclarationsInHeader( |
|
const protobuf::Descriptor* message, Output& output); |
|
void WriteDefaultInstanceHeader(const protobuf::Descriptor* message, |
|
Output& output); |
|
void WriteExtensionIdentifiersImplementation( |
|
const protobuf::Descriptor* message, |
|
const std::vector<const protobuf::FieldDescriptor*>& file_exts, |
|
Output& output); |
|
void WriteUsingEnumsInHeader( |
|
const protobuf::Descriptor* message, |
|
const std::vector<const protobuf::EnumDescriptor*>& file_enums, |
|
Output& output); |
|
|
|
// Writes message class declarations into .upb.proto.h. |
|
// |
|
// For each proto Foo, FooAccess and FooProxy/FooCProxy are generated |
|
// that are exposed to users as Foo , Ptr<Foo> and Ptr<const Foo>. |
|
void WriteMessageClassDeclarations( |
|
const protobuf::Descriptor* descriptor, |
|
const std::vector<const protobuf::FieldDescriptor*>& file_exts, |
|
const std::vector<const protobuf::EnumDescriptor*>& file_enums, |
|
Output& output) { |
|
if (IsMapEntryMessage(descriptor)) { |
|
// Skip map entry generation. Low level accessors for maps are |
|
// generated that don't require a separate map type. |
|
return; |
|
} |
|
|
|
// Forward declaration of Proto Class for GCC handling of free friend method. |
|
output("class $0;\n", ClassName(descriptor)); |
|
output("namespace internal {\n\n"); |
|
WriteModelAccessDeclaration(descriptor, output); |
|
output("\n"); |
|
WriteInternalForwardDeclarationsInHeader(descriptor, output); |
|
output("\n"); |
|
output("} // namespace internal\n\n"); |
|
WriteModelPublicDeclaration(descriptor, file_exts, file_enums, output); |
|
output("namespace internal {\n"); |
|
WriteModelCProxyDeclaration(descriptor, output); |
|
WriteModelProxyDeclaration(descriptor, output); |
|
output("} // namespace internal\n\n"); |
|
} |
|
|
|
void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor, |
|
Output& output) { |
|
output( |
|
R"cc( |
|
class $0Access { |
|
public: |
|
$0Access() {} |
|
$0Access($1* msg, upb_Arena* arena) : msg_(msg), arena_(arena) { |
|
assert(arena != nullptr); |
|
} // NOLINT |
|
$0Access(const $1* msg, upb_Arena* arena) |
|
: msg_(const_cast<$1*>(msg)), arena_(arena) { |
|
assert(arena != nullptr); |
|
} // NOLINT |
|
void* GetInternalArena() const { return arena_; } |
|
)cc", |
|
ClassName(descriptor), MessageName(descriptor)); |
|
WriteFieldAccessorsInHeader(descriptor, output); |
|
WriteOneofAccessorsInHeader(descriptor, output); |
|
output.Indent(); |
|
output( |
|
R"cc( |
|
private: |
|
friend class $2; |
|
friend class $0Proxy; |
|
friend class $0CProxy; |
|
friend struct ::protos::internal::PrivateAccess; |
|
$1* msg_; |
|
upb_Arena* arena_; |
|
)cc", |
|
ClassName(descriptor), MessageName(descriptor), |
|
QualifiedClassName(descriptor)); |
|
output.Outdent(); |
|
output("};\n"); |
|
} |
|
|
|
std::string UnderscoresToCamelCase(absl::string_view input, |
|
bool cap_next_letter) { |
|
std::string result; |
|
|
|
for (size_t i = 0; i < input.size(); i++) { |
|
if (absl::ascii_islower(input[i])) { |
|
if (cap_next_letter) { |
|
result += absl::ascii_toupper(input[i]); |
|
} else { |
|
result += input[i]; |
|
} |
|
cap_next_letter = false; |
|
} else if (absl::ascii_isupper(input[i])) { |
|
// Capital letters are left as-is. |
|
result += input[i]; |
|
cap_next_letter = false; |
|
} else if (absl::ascii_isdigit(input[i])) { |
|
result += input[i]; |
|
cap_next_letter = true; |
|
} else { |
|
cap_next_letter = true; |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
std::string FieldConstantName(const protobuf::FieldDescriptor* field) { |
|
std::string field_name = UnderscoresToCamelCase(field->name(), true); |
|
std::string result = absl::StrCat("k", field_name, "FieldNumber"); |
|
|
|
if (!field->is_extension() && |
|
field->containing_type()->FindFieldByCamelcaseName( |
|
field->camelcase_name()) != field) { |
|
// This field's camelcase name is not unique, add field number to make it |
|
// unique. |
|
absl::StrAppend(&result, "_", field->number()); |
|
} |
|
return result; |
|
} |
|
|
|
void WriteConstFieldNumbers(Output& output, |
|
const protobuf::Descriptor* descriptor) { |
|
for (auto field : FieldRange(descriptor)) { |
|
output("static constexpr ::uint32_t $0 = $1;\n", FieldConstantName(field), |
|
field->number()); |
|
} |
|
output("\n\n"); |
|
} |
|
|
|
void WriteModelPublicDeclaration( |
|
const protobuf::Descriptor* descriptor, |
|
const std::vector<const protobuf::FieldDescriptor*>& file_exts, |
|
const std::vector<const protobuf::EnumDescriptor*>& file_enums, |
|
Output& output) { |
|
output( |
|
R"cc( |
|
class $0 final : private internal::$0Access { |
|
public: |
|
using Access = internal::$0Access; |
|
using Proxy = internal::$0Proxy; |
|
using CProxy = internal::$0CProxy; |
|
|
|
$0(); |
|
|
|
$0(const $0& from); |
|
$0& operator=(const $3& from); |
|
$0(const CProxy& from); |
|
$0(const Proxy& from); |
|
$0& operator=(const CProxy& from); |
|
|
|
$0($0&& m) |
|
: Access(std::exchange(m.msg_, nullptr), |
|
std::exchange(m.arena_, nullptr)), |
|
owned_arena_(std::move(m.owned_arena_)) {} |
|
|
|
$0& operator=($0&& m) { |
|
msg_ = std::exchange(m.msg_, nullptr); |
|
arena_ = std::exchange(m.arena_, nullptr); |
|
owned_arena_ = std::move(m.owned_arena_); |
|
return *this; |
|
} |
|
)cc", |
|
ClassName(descriptor), |
|
::upb::generator::MessageInit(descriptor->full_name()), |
|
MessageName(descriptor), QualifiedClassName(descriptor)); |
|
|
|
WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessage, output); |
|
WriteUsingEnumsInHeader(descriptor, file_enums, output); |
|
WriteDefaultInstanceHeader(descriptor, output); |
|
WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output); |
|
if (descriptor->extension_range_count()) { |
|
// for typetrait checking |
|
output("using ExtendableType = $0;\n", ClassName(descriptor)); |
|
} |
|
// Note: free function friends that are templates such as ::protos::Parse |
|
// require explicit <$2> type parameter in declaration to be able to compile |
|
// with gcc otherwise the compiler will fail with |
|
// "has not been declared within namespace" error. Even though there is a |
|
// namespace qualifier, cross namespace matching fails. |
|
output.Indent(); |
|
output( |
|
R"cc( |
|
static const upb_MiniTable* minitable(); |
|
using $0Access::GetInternalArena; |
|
)cc", |
|
ClassName(descriptor)); |
|
output("\n"); |
|
WriteConstFieldNumbers(output, descriptor); |
|
output( |
|
R"cc( |
|
private: |
|
const upb_Message* msg() const { return UPB_UPCAST(msg_); } |
|
upb_Message* msg() { return UPB_UPCAST(msg_); } |
|
|
|
$0(upb_Message* msg, upb_Arena* arena) : $0Access() { |
|
msg_ = ($1*)msg; |
|
arena_ = owned_arena_.ptr(); |
|
upb_Arena_Fuse(arena_, arena); |
|
} |
|
::protos::Arena owned_arena_; |
|
friend struct ::protos::internal::PrivateAccess; |
|
friend Proxy; |
|
friend CProxy; |
|
friend absl::StatusOr<$2>(::protos::Parse<$2>(absl::string_view bytes, |
|
int options)); |
|
friend absl::StatusOr<$2>(::protos::Parse<$2>( |
|
absl::string_view bytes, |
|
const ::protos::ExtensionRegistry& extension_registry, |
|
int options)); |
|
friend upb_Arena* ::protos::internal::GetArena<$0>($0* message); |
|
friend upb_Arena* ::protos::internal::GetArena<$0>(::protos::Ptr<$0> message); |
|
friend $0(::hpb::internal::MoveMessage<$0>(upb_Message* msg, upb_Arena* arena)); |
|
)cc", |
|
ClassName(descriptor), MessageName(descriptor), |
|
QualifiedClassName(descriptor)); |
|
output.Outdent(); |
|
output("};\n\n"); |
|
} |
|
|
|
void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor, |
|
Output& output) { |
|
// Foo::Proxy. |
|
output( |
|
R"cc( |
|
class $0Proxy final : private internal::$0Access { |
|
public: |
|
$0Proxy() = delete; |
|
$0Proxy(const $0Proxy& m) : internal::$0Access() { |
|
msg_ = m.msg_; |
|
arena_ = m.arena_; |
|
} |
|
$0Proxy($0* m) : internal::$0Access() { |
|
msg_ = m->msg_; |
|
arena_ = m->arena_; |
|
} |
|
$0Proxy operator=(const $0Proxy& m) { |
|
msg_ = m.msg_; |
|
arena_ = m.arena_; |
|
return *this; |
|
} |
|
using $0Access::GetInternalArena; |
|
)cc", |
|
ClassName(descriptor)); |
|
|
|
WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy, |
|
output); |
|
output("\n"); |
|
output.Indent(1); |
|
output( |
|
R"cc( |
|
private: |
|
upb_Message* msg() const { return UPB_UPCAST(msg_); } |
|
|
|
$0Proxy(upb_Message* msg, upb_Arena* arena) |
|
: internal::$0Access(($1*)msg, arena) {} |
|
friend $0::Proxy(::protos::CreateMessage<$0>(::protos::Arena& arena)); |
|
friend $0::Proxy(::protos::internal::CreateMessageProxy<$0>( |
|
upb_Message*, upb_Arena*)); |
|
friend struct ::protos::internal::PrivateAccess; |
|
friend class RepeatedFieldProxy; |
|
friend class $0CProxy; |
|
friend class $0Access; |
|
friend class ::protos::Ptr<$0>; |
|
friend class ::protos::Ptr<const $0>; |
|
static const upb_MiniTable* minitable() { return $0::minitable(); } |
|
friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0Proxy>( |
|
const $0Proxy* message); |
|
friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0Proxy>( |
|
::protos::Ptr<$0Proxy> message); |
|
friend upb_Arena* ::protos::internal::GetArena<$2>($2* message); |
|
friend upb_Arena* ::protos::internal::GetArena<$2>(::protos::Ptr<$2> message); |
|
static void Rebind($0Proxy& lhs, const $0Proxy& rhs) { |
|
lhs.msg_ = rhs.msg_; |
|
lhs.arena_ = rhs.arena_; |
|
} |
|
)cc", |
|
ClassName(descriptor), MessageName(descriptor), |
|
QualifiedClassName(descriptor)); |
|
output.Outdent(1); |
|
output("};\n\n"); |
|
} |
|
|
|
void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor, |
|
Output& output) { |
|
// Foo::CProxy. |
|
output( |
|
R"cc( |
|
class $0CProxy final : private internal::$0Access { |
|
public: |
|
$0CProxy() = delete; |
|
$0CProxy(const $0* m) |
|
: internal::$0Access(m->msg_, ::protos::internal::GetArena(m)) {} |
|
$0CProxy($0Proxy m); |
|
using $0Access::GetInternalArena; |
|
)cc", |
|
ClassName(descriptor), MessageName(descriptor)); |
|
|
|
WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageCProxy, |
|
output); |
|
|
|
output.Indent(1); |
|
output( |
|
R"cc( |
|
private: |
|
using AsNonConst = $0Proxy; |
|
const upb_Message* msg() const { return UPB_UPCAST(msg_); } |
|
|
|
$0CProxy(const upb_Message* msg, upb_Arena* arena) |
|
: internal::$0Access(($1*)msg, arena){}; |
|
friend struct ::protos::internal::PrivateAccess; |
|
friend class RepeatedFieldProxy; |
|
friend class ::protos::Ptr<$0>; |
|
friend class ::protos::Ptr<const $0>; |
|
static const upb_MiniTable* minitable() { return $0::minitable(); } |
|
friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0CProxy>( |
|
const $0CProxy* message); |
|
friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0CProxy>( |
|
::protos::Ptr<$0CProxy> message); |
|
|
|
static void Rebind($0CProxy& lhs, const $0CProxy& rhs) { |
|
lhs.msg_ = rhs.msg_; |
|
lhs.arena_ = rhs.arena_; |
|
} |
|
)cc", |
|
ClassName(descriptor), MessageName(descriptor)); |
|
output.Outdent(1); |
|
output("};\n\n"); |
|
} |
|
|
|
void WriteDefaultInstanceHeader(const protobuf::Descriptor* message, |
|
Output& output) { |
|
output(" static ::protos::Ptr<const $0> default_instance();\n", |
|
ClassName(message)); |
|
} |
|
|
|
void WriteMessageImplementation( |
|
const protobuf::Descriptor* descriptor, |
|
const std::vector<const protobuf::FieldDescriptor*>& file_exts, |
|
Output& output) { |
|
bool message_is_map_entry = descriptor->options().map_entry(); |
|
if (!message_is_map_entry) { |
|
// Constructor. |
|
output( |
|
R"cc( |
|
$0::$0() : $0Access() { |
|
arena_ = owned_arena_.ptr(); |
|
msg_ = $1_new(arena_); |
|
} |
|
$0::$0(const $0& from) : $0Access() { |
|
arena_ = owned_arena_.ptr(); |
|
msg_ = ($1*)::protos::internal::DeepClone(UPB_UPCAST(from.msg_), &$2, arena_); |
|
} |
|
$0::$0(const CProxy& from) : $0Access() { |
|
arena_ = owned_arena_.ptr(); |
|
msg_ = ($1*)::protos::internal::DeepClone( |
|
::protos::internal::GetInternalMsg(&from), &$2, arena_); |
|
} |
|
$0::$0(const Proxy& from) : $0(static_cast<const CProxy&>(from)) {} |
|
internal::$0CProxy::$0CProxy($0Proxy m) : $0Access() { |
|
arena_ = m.arena_; |
|
msg_ = ($1*)::protos::internal::GetInternalMsg(&m); |
|
} |
|
$0& $0::operator=(const $3& from) { |
|
arena_ = owned_arena_.ptr(); |
|
msg_ = ($1*)::protos::internal::DeepClone(UPB_UPCAST(from.msg_), &$2, arena_); |
|
return *this; |
|
} |
|
$0& $0::operator=(const CProxy& from) { |
|
arena_ = owned_arena_.ptr(); |
|
msg_ = ($1*)::protos::internal::DeepClone( |
|
::protos::internal::GetInternalMsg(&from), &$2, arena_); |
|
return *this; |
|
} |
|
)cc", |
|
ClassName(descriptor), MessageName(descriptor), |
|
::upb::generator::MessageInit(descriptor->full_name()), |
|
QualifiedClassName(descriptor)); |
|
output("\n"); |
|
// Minitable |
|
output( |
|
R"cc( |
|
const upb_MiniTable* $0::minitable() { return &$1; } |
|
)cc", |
|
ClassName(descriptor), |
|
::upb::generator::MessageInit(descriptor->full_name())); |
|
output("\n"); |
|
} |
|
|
|
WriteAccessorsInSource(descriptor, output); |
|
|
|
if (!message_is_map_entry) { |
|
output( |
|
R"cc( |
|
struct $0DefaultTypeInternal { |
|
$1* msg; |
|
upb_Arena* arena; |
|
}; |
|
static $0DefaultTypeInternal _$0DefaultTypeBuilder() { |
|
upb_Arena* arena = upb_Arena_New(); |
|
return $0DefaultTypeInternal{$1_new(arena), arena}; |
|
} |
|
$0DefaultTypeInternal _$0_default_instance_ = _$0DefaultTypeBuilder(); |
|
)cc", |
|
ClassName(descriptor), MessageName(descriptor)); |
|
|
|
output( |
|
R"cc( |
|
::protos::Ptr<const $0> $0::default_instance() { |
|
return ::protos::internal::CreateMessage<$0>( |
|
(upb_Message *)_$0_default_instance_.msg, |
|
_$0_default_instance_.arena); |
|
} |
|
)cc", |
|
ClassName(descriptor)); |
|
|
|
WriteExtensionIdentifiersImplementation(descriptor, file_exts, output); |
|
} |
|
} |
|
|
|
void WriteInternalForwardDeclarationsInHeader( |
|
const protobuf::Descriptor* message, Output& output) { |
|
// Write declaration for internal re-usable default_instance without |
|
// leaking implementation. |
|
output( |
|
R"cc( |
|
struct $0DefaultTypeInternal; |
|
extern $0DefaultTypeInternal _$0_default_instance_; |
|
)cc", |
|
ClassName(message)); |
|
} |
|
|
|
void WriteExtensionIdentifiersInClassHeader( |
|
const protobuf::Descriptor* message, |
|
const std::vector<const protobuf::FieldDescriptor*>& file_exts, |
|
Output& output) { |
|
for (auto* ext : file_exts) { |
|
if (ext->extension_scope() && |
|
ext->extension_scope()->full_name() == message->full_name()) { |
|
WriteExtensionIdentifierHeader(ext, output); |
|
} |
|
} |
|
} |
|
|
|
void WriteExtensionIdentifiersImplementation( |
|
const protobuf::Descriptor* message, |
|
const std::vector<const protobuf::FieldDescriptor*>& file_exts, |
|
Output& output) { |
|
for (auto* ext : file_exts) { |
|
if (ext->extension_scope() && |
|
ext->extension_scope()->full_name() == message->full_name()) { |
|
WriteExtensionIdentifier(ext, output); |
|
} |
|
} |
|
} |
|
|
|
void WriteUsingEnumsInHeader( |
|
const protobuf::Descriptor* message, |
|
const std::vector<const protobuf::EnumDescriptor*>& file_enums, |
|
Output& output) { |
|
for (auto* enum_descriptor : file_enums) { |
|
std::string enum_type_name = EnumTypeName(enum_descriptor); |
|
std::string enum_resolved_type_name = |
|
enum_descriptor->file()->package().empty() && |
|
enum_descriptor->containing_type() == nullptr |
|
? absl::StrCat(kNoPackageNamePrefix, |
|
ToCIdent(enum_descriptor->name())) |
|
: enum_type_name; |
|
if (enum_descriptor->containing_type() == nullptr || |
|
enum_descriptor->containing_type()->full_name() != |
|
message->full_name()) { |
|
continue; |
|
} |
|
output("using $0", enum_descriptor->name()); |
|
if (enum_descriptor->options().deprecated()) { |
|
output(" ABSL_DEPRECATED(\"Proto enum $0\")", enum_descriptor->name()); |
|
} |
|
output(" = $0;", enum_resolved_type_name); |
|
output("\n"); |
|
int value_count = enum_descriptor->value_count(); |
|
for (int i = 0; i < value_count; i++) { |
|
output("static constexpr $0 $1", enum_descriptor->name(), |
|
enum_descriptor->value(i)->name()); |
|
if (enum_descriptor->options().deprecated() || |
|
enum_descriptor->value(i)->options().deprecated()) { |
|
output(" ABSL_DEPRECATED(\"Proto enum value $0\") ", |
|
enum_descriptor->value(i)->name()); |
|
} |
|
output(" = $0;\n", EnumValueSymbolInNameSpace(enum_descriptor, |
|
enum_descriptor->value(i))); |
|
} |
|
} |
|
} |
|
|
|
} // namespace protobuf |
|
} // namespace google::hpb_generator
|
|
|