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.
677 lines
28 KiB
677 lines
28 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_accessors.h" |
|
|
|
#include <string> |
|
|
|
#include "absl/container/flat_hash_set.h" |
|
#include "absl/strings/ascii.h" |
|
#include "absl/strings/match.h" |
|
#include "absl/strings/str_cat.h" |
|
#include "absl/strings/string_view.h" |
|
#include "google/protobuf/compiler/hpb/context.h" |
|
#include "google/protobuf/compiler/hpb/gen_repeated_fields.h" |
|
#include "google/protobuf/compiler/hpb/gen_utils.h" |
|
#include "google/protobuf/compiler/hpb/keywords.h" |
|
#include "google/protobuf/compiler/hpb/names.h" |
|
#include "google/protobuf/descriptor.h" |
|
#include "upb_generator/c/names.h" |
|
#include "upb_generator/minitable/names.h" |
|
|
|
namespace google::protobuf::hpb_generator { |
|
|
|
namespace protobuf = ::proto2; |
|
|
|
using NameToFieldDescriptorMap = |
|
absl::flat_hash_map<absl::string_view, const protobuf::FieldDescriptor*>; |
|
|
|
void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc, |
|
const protobuf::FieldDescriptor* field, |
|
absl::string_view resolved_field_name, |
|
absl::string_view resolved_upbc_name, |
|
Context& ctx); |
|
void WriteFieldAccessorClear(const protobuf::Descriptor* desc, |
|
const protobuf::FieldDescriptor* field, |
|
absl::string_view resolved_field_name, |
|
absl::string_view resolved_upbc_name, |
|
Context& ctx); |
|
void WriteMapFieldAccessors(const protobuf::Descriptor* desc, |
|
const protobuf::FieldDescriptor* field, |
|
absl::string_view resolved_field_name, |
|
absl::string_view resolved_upbc_name, Context& ctx); |
|
|
|
void WriteMapAccessorDefinitions(const protobuf::Descriptor* message, |
|
const protobuf::FieldDescriptor* field, |
|
absl::string_view resolved_field_name, |
|
absl::string_view class_name, Context& ctx); |
|
|
|
// 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); |
|
|
|
upb::generator::NameMangler CreateNameMangler( |
|
const protobuf::Descriptor* message) { |
|
return upb::generator::NameMangler(upb::generator::GetCppFields(message)); |
|
} |
|
|
|
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, |
|
Context& ctx) { |
|
// Generate const methods. |
|
auto field_names = CreateFieldNameMap(desc); |
|
auto mangler = CreateNameMangler(desc); |
|
|
|
auto indent = ctx.printer().WithIndent(); |
|
|
|
for (const auto* field : FieldNumberOrder(desc)) { |
|
std::string resolved_field_name = ResolveFieldName(field, field_names); |
|
std::string resolved_upbc_name = mangler.ResolveFieldName(field->name()); |
|
WriteFieldAccessorHazzer(desc, field, resolved_field_name, |
|
resolved_upbc_name, ctx); |
|
WriteFieldAccessorClear(desc, field, resolved_field_name, |
|
resolved_upbc_name, ctx); |
|
|
|
if (field->is_map()) { |
|
WriteMapFieldAccessors(desc, field, resolved_field_name, |
|
resolved_upbc_name, ctx); |
|
} else if (desc->options().map_entry()) { |
|
// TODO Implement map entry |
|
} else if (field->is_repeated()) { |
|
WriteRepeatedFieldsInMessageHeader(desc, field, resolved_field_name, |
|
resolved_upbc_name, ctx); |
|
} else { |
|
// non-repeated. |
|
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) { |
|
ctx.EmitLegacy(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) { |
|
ctx.EmitLegacy(R"cc( |
|
$1 $2() const; |
|
$0 mutable_$2(); |
|
/** |
|
* Re-points submessage to the given target. |
|
* |
|
* REQUIRES: |
|
* - both messages must be in the same arena, or in two |
|
* fused arenas. |
|
*/ |
|
void set_alias_$2($0 target); |
|
)cc", |
|
MessagePtrConstType(field, /* const */ false), |
|
MessagePtrConstType(field, /* const */ true), |
|
resolved_field_name); |
|
} else { |
|
ctx.EmitLegacy( |
|
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, |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
resolved_upbc_name); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc, |
|
const protobuf::FieldDescriptor* field, |
|
const absl::string_view resolved_field_name, |
|
const absl::string_view resolved_upbc_name, |
|
Context& ctx) { |
|
// Generate hazzer (if any). |
|
if (field->has_presence()) { |
|
// Has presence. |
|
ctx.EmitLegacy("inline bool has_$0() const { return $1_has_$2(msg_); }\n", |
|
resolved_field_name, |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
resolved_upbc_name); |
|
} |
|
} |
|
|
|
void WriteFieldAccessorClear(const protobuf::Descriptor* desc, |
|
const protobuf::FieldDescriptor* field, |
|
const absl::string_view resolved_field_name, |
|
const absl::string_view resolved_upbc_name, |
|
Context& ctx) { |
|
if (field->has_presence()) { |
|
ctx.EmitLegacy("void clear_$0() { $2_clear_$1(msg_); }\n", |
|
resolved_field_name, resolved_upbc_name, |
|
upb::generator::CApiMessageType(desc->full_name())); |
|
} |
|
} |
|
|
|
void WriteMapFieldAccessors(const protobuf::Descriptor* desc, |
|
const protobuf::FieldDescriptor* field, |
|
const absl::string_view resolved_field_name, |
|
const absl::string_view resolved_upbc_name, |
|
Context& ctx) { |
|
const protobuf::Descriptor* entry = field->message_type(); |
|
const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1); |
|
const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2); |
|
ctx.EmitLegacy( |
|
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, upb::generator::CApiMessageType(desc->full_name()), |
|
CppConstType(key), resolved_upbc_name); |
|
|
|
if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { |
|
ctx.Emit({{"field_name", resolved_field_name}, |
|
{"const_key", CppConstType(key)}, |
|
{"const_val", CppConstType(val)}, |
|
{"ConstPtr", MessagePtrConstType(val, true)}, |
|
{"MutPtr", MessagePtrConstType(val, false)}}, |
|
R"cc( |
|
bool set_$field_name$($const_key$ key, $ConstPtr$ value); |
|
bool set_$field_name$($const_key$ key, $MutPtr$ value); |
|
bool set_alias_$field_name$($const_key$ key, $ConstPtr$ value); |
|
bool set_alias_$field_name$($const_key$ key, $MutPtr$ value); |
|
absl::StatusOr<$ConstPtr$> get_$field_name$($const_key$ key); |
|
absl::StatusOr<$MutPtr$> get_mutable_$field_name$($const_key$ key); |
|
)cc"); |
|
} else { |
|
ctx.EmitLegacy( |
|
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, Context& ctx) { |
|
std::string class_name = ClassName(desc); |
|
absl::StrAppend(&class_name, "Access"); |
|
ctx.Emit("namespace internal {\n"); |
|
const char arena_expression[] = "arena_"; |
|
auto field_names = CreateFieldNameMap(desc); |
|
auto mangler = CreateNameMangler(desc); |
|
|
|
// Generate const methods. |
|
auto indent = ctx.printer().WithIndent(); |
|
for (const auto* field : FieldNumberOrder(desc)) { |
|
std::string resolved_field_name = ResolveFieldName(field, field_names); |
|
std::string resolved_upbc_name = mangler.ResolveFieldName(field->name()); |
|
if (field->is_map()) { |
|
WriteMapAccessorDefinitions(desc, field, resolved_field_name, class_name, |
|
ctx); |
|
} else if (desc->options().map_entry()) { |
|
// TODO Implement map entry |
|
} else if (field->is_repeated()) { |
|
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { |
|
WriteRepeatedMessageAccessor(desc, field, resolved_field_name, |
|
class_name, ctx); |
|
} else if (field->cpp_type() == |
|
protobuf::FieldDescriptor::CPPTYPE_STRING) { |
|
WriteRepeatedStringAccessor(desc, field, resolved_field_name, |
|
class_name, ctx); |
|
} else { |
|
WriteRepeatedScalarAccessor(desc, field, resolved_field_name, |
|
class_name, ctx); |
|
} |
|
} else { |
|
// non-repeated field. |
|
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) { |
|
ctx.EmitLegacy( |
|
R"cc( |
|
$1 $0::$2() const { |
|
return hpb::interop::upb::FromUpbStringView($3_$4(msg_)); |
|
} |
|
)cc", |
|
class_name, CppConstType(field), resolved_field_name, |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
resolved_upbc_name); |
|
// Set string. |
|
ctx.EmitLegacy( |
|
R"cc( |
|
void $0::set_$2($1 value) { |
|
$4_set_$3(msg_, hpb::interop::upb::CopyToUpbStringView(value, $5)); |
|
} |
|
)cc", |
|
class_name, CppConstType(field), resolved_field_name, |
|
resolved_upbc_name, |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
arena_expression); |
|
} else if (field->cpp_type() == |
|
protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { |
|
ctx.EmitLegacy( |
|
R"cc( |
|
$1 $0::$2() const { |
|
if (!has_$2()) { |
|
return $4::default_instance(); |
|
} |
|
return ::hpb::interop::upb::MakeCHandle<$4>( |
|
(upb_Message*)($3_$5(msg_)), arena_); |
|
} |
|
)cc", |
|
class_name, MessagePtrConstType(field, /* is_const */ true), |
|
resolved_field_name, |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
MessageBaseType(field, /* maybe_const */ false), |
|
resolved_upbc_name); |
|
|
|
ctx.EmitLegacy( |
|
R"cc( |
|
$1 $0::mutable_$2() { |
|
return hpb::interop::upb::MakeHandle<$4>( |
|
(upb_Message*)($3_mutable_$5(msg_, $6)), $6); |
|
} |
|
void $0::set_alias_$2($1 target) { |
|
ABSL_CHECK(upb_Arena_IsFused(arena_, hpb::interop::upb::GetArena(target))); |
|
upb_Message_SetBaseFieldMessage( |
|
UPB_UPCAST(msg_), |
|
upb_MiniTable_GetFieldByIndex($7::minitable(), $8), |
|
hpb::interop::upb::GetMessage(target)); |
|
} |
|
)cc", |
|
class_name, MessagePtrConstType(field, /* is_const */ false), |
|
resolved_field_name, |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
MessageBaseType(field, /* maybe_const */ false), resolved_upbc_name, |
|
arena_expression, ClassName(desc), ctx.GetLayoutIndex(field)); |
|
} |
|
} |
|
} |
|
ctx.Emit("\n"); |
|
ctx.Emit("} // namespace internal\n\n"); |
|
} |
|
|
|
void WriteMapAccessorDefinitions(const protobuf::Descriptor* desc, |
|
const protobuf::FieldDescriptor* field, |
|
const absl::string_view resolved_field_name, |
|
const absl::string_view class_name, |
|
Context& ctx) { |
|
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) { |
|
ctx.EmitLegacy( |
|
R"cc( |
|
bool $0::set_$1($2 key, $3 value) { |
|
upb_Message* clone = upb_Message_DeepClone( |
|
::hpb::internal::PrivateAccess::GetInternalMsg(value), &$9, |
|
arena_); |
|
$6return $4_$8_set(msg_, $7, ($5*)clone, arena_); |
|
} |
|
)cc", |
|
class_name, resolved_field_name, CppConstType(key), |
|
MessagePtrConstType(val, /* is_const */ true), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
upb::generator::CApiMessageType(val->message_type()->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name, |
|
::upb::generator::MiniTableMessageVarName( |
|
val->message_type()->full_name())); |
|
ctx.EmitLegacy( |
|
R"cc( |
|
bool $0::set_$1($2 key, $3 value) { |
|
upb_Message* clone = upb_Message_DeepClone( |
|
::hpb::internal::PrivateAccess::GetInternalMsg(value), &$9, |
|
arena_); |
|
$6return $4_$8_set(msg_, $7, ($5*)clone, arena_); |
|
} |
|
)cc", |
|
class_name, resolved_field_name, CppConstType(key), |
|
MessagePtrConstType(val, /* is_const */ false), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
upb::generator::CApiMessageType(val->message_type()->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name, |
|
::upb::generator::MiniTableMessageVarName( |
|
val->message_type()->full_name())); |
|
ctx.EmitLegacy( |
|
R"cc( |
|
bool $0::set_alias_$1($2 key, $3 value) { |
|
$6return $4_$8_set( |
|
msg_, $7, ($5*)hpb::interop::upb::GetMessage(value), arena_); |
|
} |
|
)cc", |
|
class_name, resolved_field_name, CppConstType(key), |
|
MessagePtrConstType(val, /* is_const */ true), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
upb::generator::CApiMessageType(val->message_type()->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name); |
|
ctx.EmitLegacy( |
|
R"cc( |
|
bool $0::set_alias_$1($2 key, $3 value) { |
|
$6return $4_$8_set( |
|
msg_, $7, ($5*)hpb::interop::upb::GetMessage(value), arena_); |
|
} |
|
)cc", |
|
class_name, resolved_field_name, CppConstType(key), |
|
MessagePtrConstType(val, /* is_const */ false), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
upb::generator::CApiMessageType(val->message_type()->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name); |
|
ctx.EmitLegacy( |
|
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 ::hpb::interop::upb::MakeCHandle<$6>(UPB_UPCAST(msg_value), arena_); |
|
} |
|
return absl::NotFoundError(""); |
|
} |
|
)cc", |
|
class_name, resolved_field_name, CppConstType(key), |
|
MessagePtrConstType(val, /* is_const */ true), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
upb::generator::CApiMessageType(val->message_type()->full_name()), |
|
QualifiedClassName(val->message_type()), optional_conversion_code, |
|
converted_key_name, upbc_name); |
|
ctx.Emit( |
|
{{"class_name", class_name}, |
|
{"hpb_field_name", resolved_field_name}, |
|
{"const_key", CppConstType(key)}, |
|
{"PtrMut", MessagePtrConstType(val, false)}, |
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())}, |
|
{"return_type", |
|
upb::generator::CApiMessageType(val->message_type()->full_name())}, |
|
{"proto_class", QualifiedClassName(val->message_type())}, |
|
{"optional_conversion_code", optional_conversion_code}, |
|
{"converted_key_name", converted_key_name}, |
|
{"upb_field_name", upbc_name}}, |
|
R"cc( |
|
absl::StatusOr<$PtrMut$> $class_name$::get_mutable_$hpb_field_name$( |
|
$const_key$ key) { |
|
$return_type$* msg_value; |
|
$optional_conversion_code$bool success = |
|
$upb_msg_name$_$upb_field_name$_get(msg_, $converted_key_name$, |
|
&msg_value); |
|
if (success) { |
|
return ::hpb::interop::upb::MakeHandle<$proto_class$>( |
|
UPB_UPCAST(msg_value), arena_); |
|
} |
|
return absl::NotFoundError(""); |
|
} |
|
)cc"); |
|
ctx.EmitLegacy( |
|
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), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
upb::generator::CApiMessageType(val->message_type()->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name); |
|
} else if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) { |
|
ctx.EmitLegacy( |
|
R"cc( |
|
bool $0::set_$1($2 key, $3 value) { |
|
$5return $4_$7_set( |
|
msg_, $6, hpb::interop::upb::CopyToUpbStringView(value, arena_), |
|
arena_); |
|
} |
|
)cc", |
|
class_name, resolved_field_name, CppConstType(key), CppConstType(val), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name); |
|
ctx.EmitLegacy( |
|
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), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name); |
|
ctx.EmitLegacy( |
|
R"cc( |
|
void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); } |
|
)cc", |
|
class_name, resolved_field_name, CppConstType(key), CppConstType(val), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name); |
|
} else { |
|
ctx.EmitLegacy( |
|
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), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name); |
|
ctx.EmitLegacy( |
|
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), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name); |
|
ctx.EmitLegacy( |
|
R"cc( |
|
void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); } |
|
)cc", |
|
class_name, resolved_field_name, CppConstType(key), CppConstType(val), |
|
upb::generator::CApiMessageType(desc->full_name()), |
|
optional_conversion_code, converted_key_name, upbc_name); |
|
} |
|
} |
|
|
|
void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc, |
|
MessageClassType handle_type, Context& ctx) { |
|
bool read_only = handle_type == MessageClassType::kMessageCProxy; |
|
|
|
// Generate const methods. |
|
auto indent = ctx.printer().WithIndent(); |
|
std::string class_name = ClassName(desc); |
|
auto field_names = CreateFieldNameMap(desc); |
|
|
|
for (const auto* field : FieldNumberOrder(desc)) { |
|
std::string resolved_field_name = ResolveFieldName(field, field_names); |
|
// Generate hazzer (if any). |
|
if (field->has_presence()) { |
|
ctx.EmitLegacy("using $0Access::has_$1;\n", class_name, |
|
resolved_field_name); |
|
if (!read_only) { |
|
ctx.EmitLegacy("using $0Access::clear_$1;\n", class_name, |
|
resolved_field_name); |
|
} |
|
} |
|
if (field->is_map()) { |
|
ctx.EmitLegacy( |
|
R"cc( |
|
using $0Access::$1_size; |
|
using $0Access::get_$1; |
|
)cc", |
|
class_name, resolved_field_name); |
|
if (!read_only) { |
|
ctx.EmitLegacy( |
|
R"cc( |
|
using $0Access::clear_$1; |
|
using $0Access::delete_$1; |
|
using $0Access::set_$1; |
|
)cc", |
|
class_name, resolved_field_name); |
|
// only emit set_alias and get_mutable for maps when value is a message |
|
if (field->message_type()->FindFieldByNumber(2)->cpp_type() == |
|
protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { |
|
ctx.Emit( |
|
{{"class_name", class_name}, {"field_name", resolved_field_name}}, |
|
R"cc( |
|
using $class_name$Access::get_mutable_$field_name$; |
|
using $class_name$Access::set_alias_$field_name$; |
|
)cc"); |
|
} |
|
} |
|
} else if (desc->options().map_entry()) { |
|
// TODO Implement map entry |
|
} else if (field->is_repeated()) { |
|
WriteRepeatedFieldUsingAccessors(field, class_name, resolved_field_name, |
|
ctx, read_only); |
|
} else { |
|
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { |
|
ctx.EmitLegacy("using $0Access::$1;\n", ClassName(desc), |
|
resolved_field_name); |
|
if (!read_only) { |
|
ctx.EmitLegacy("using $0Access::mutable_$1;\n", class_name, |
|
resolved_field_name); |
|
ctx.EmitLegacy("using $0Access::set_alias_$1;\n", class_name, |
|
resolved_field_name); |
|
} |
|
} else { |
|
ctx.EmitLegacy("using $0Access::$1;\n", class_name, |
|
resolved_field_name); |
|
if (!read_only) { |
|
ctx.EmitLegacy("using $0Access::set_$1;\n", class_name, |
|
resolved_field_name); |
|
} |
|
} |
|
} |
|
} |
|
for (int i = 0; i < desc->real_oneof_decl_count(); ++i) { |
|
const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i); |
|
ctx.EmitLegacy("using $0Access::$1_case;\n", class_name, oneof->name()); |
|
ctx.EmitLegacy("using $0Access::$1Case;\n", class_name, |
|
ToCamelCase(oneof->name(), /*lower_first=*/false)); |
|
for (int j = 0; j < oneof->field_count(); ++j) { |
|
const protobuf::FieldDescriptor* field = oneof->field(j); |
|
ctx.EmitLegacy("using $0Access::k$1;\n", class_name, |
|
ToCamelCase(field->name(), /*lower_first=*/false), |
|
field->number()); |
|
} |
|
ctx.EmitLegacy("using $0Access::$1_NOT_SET;\n", class_name, |
|
absl::AsciiStrToUpper(oneof->name())); |
|
} |
|
} |
|
|
|
void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc, |
|
Context& ctx) { |
|
// Generate const methods. |
|
auto indent = ctx.printer().WithIndent(); |
|
std::string class_name = ClassName(desc); |
|
auto field_names = CreateFieldNameMap(desc); |
|
for (int i = 0; i < desc->real_oneof_decl_count(); ++i) { |
|
const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i); |
|
ctx.EmitLegacy("enum $0Case {\n", |
|
ToCamelCase(oneof->name(), /*lower_first=*/false)); |
|
for (int j = 0; j < oneof->field_count(); ++j) { |
|
const protobuf::FieldDescriptor* field = oneof->field(j); |
|
ctx.EmitLegacy(" k$0 = $1,\n", |
|
ToCamelCase(field->name(), /*lower_first=*/false), |
|
field->number()); |
|
} |
|
ctx.EmitLegacy(" $0_NOT_SET = 0,\n", absl::AsciiStrToUpper(oneof->name())); |
|
ctx.Emit("};\n\n"); |
|
ctx.EmitLegacy("$0Case $1_case() const {\n", |
|
ToCamelCase(oneof->name(), /*lower_first=*/false), |
|
oneof->name()); |
|
for (int j = 0; j < oneof->field_count(); ++j) { |
|
const protobuf::FieldDescriptor* field = oneof->field(j); |
|
std::string resolved_field_name = ResolveFieldName(field, field_names); |
|
ctx.EmitLegacy(" if (has_$0()) { return k$1; }\n", resolved_field_name, |
|
ToCamelCase(field->name(), /*lower_first=*/false)); |
|
} |
|
ctx.EmitLegacy(" return $0_NOT_SET;\n", |
|
absl::AsciiStrToUpper(oneof->name())); |
|
ctx.Emit("}\n;"); |
|
} |
|
} |
|
|
|
std::string ResolveFieldName(const protobuf::FieldDescriptor* field, |
|
const NameToFieldDescriptorMap& field_names) { |
|
// C++ implementation specific reserved names. |
|
static const auto& kReservedNames = |
|
*new absl::flat_hash_set<absl::string_view>({ |
|
"msg", |
|
"msg_", |
|
"arena", |
|
"arena_", |
|
}); |
|
|
|
// C++ specific prefixes used by code generator for field access. |
|
static constexpr absl::string_view kClearMethodPrefix = "clear_"; |
|
static constexpr absl::string_view kSetMethodPrefix = "set_"; |
|
static constexpr absl::string_view kHasMethodPrefix = "has_"; |
|
static constexpr absl::string_view kDeleteMethodPrefix = "delete_"; |
|
static constexpr absl::string_view kAddToRepeatedMethodPrefix = "add_"; |
|
static constexpr absl::string_view kResizeArrayMethodPrefix = "resize_"; |
|
|
|
// List of generated accessor prefixes to check against. |
|
// Example: |
|
// optional repeated string phase = 236; |
|
// optional bool clear_phase = 237; |
|
static constexpr absl::string_view kAccessorPrefixes[] = { |
|
kClearMethodPrefix, kDeleteMethodPrefix, kAddToRepeatedMethodPrefix, |
|
kResizeArrayMethodPrefix, kSetMethodPrefix, kHasMethodPrefix}; |
|
|
|
absl::string_view field_name = field->name(); |
|
if (kReservedNames.count(field_name) > 0) { |
|
if (absl::EndsWith(field_name, "_")) { |
|
return absl::StrCat(field_name, "_"); |
|
} else { |
|
return absl::StrCat(field_name, "__"); |
|
} |
|
} |
|
for (const auto prefix : kAccessorPrefixes) { |
|
// If field name starts with a prefix such as clear_ and the proto |
|
// contains a field name with trailing end, depending on type of field |
|
// (repeated, map, message) we have a conflict to resolve. |
|
if (absl::StartsWith(field_name, prefix)) { |
|
auto match = field_names.find(field_name.substr(prefix.size())); |
|
if (match != field_names.end()) { |
|
const auto* candidate = match->second; |
|
if (candidate->is_repeated() || candidate->is_map() || |
|
(candidate->cpp_type() == |
|
protobuf::FieldDescriptor::CPPTYPE_STRING && |
|
prefix == kClearMethodPrefix) || |
|
prefix == kSetMethodPrefix || prefix == kHasMethodPrefix) { |
|
return absl::StrCat(field_name, "_"); |
|
} |
|
} |
|
} |
|
} |
|
return ResolveKeywordConflict(field_name); |
|
} |
|
|
|
} // namespace protobuf |
|
} // namespace google::hpb_generator
|
|
|