mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
883 lines
40 KiB
C++
883 lines
40 KiB
C++
// 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 "hpb_generator/gen_accessors.h"
|
|
|
|
#include <string>
|
|
|
|
#include "absl/container/flat_hash_map.h"
|
|
#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 "hpb_generator/context.h"
|
|
#include "hpb_generator/gen_repeated_fields.h"
|
|
#include "hpb_generator/gen_utils.h"
|
|
#include "hpb_generator/keywords.h"
|
|
#include "hpb_generator/names.h"
|
|
#include "google/protobuf/descriptor.h"
|
|
#include "google/protobuf/io/printer.h"
|
|
#include "upb_generator/c/names.h"
|
|
#include "upb_generator/minitable/names.h"
|
|
|
|
namespace google {
|
|
namespace protobuf {
|
|
namespace hpb_generator {
|
|
|
|
using NameToFieldDescriptorMap =
|
|
absl::flat_hash_map<absl::string_view, const google::protobuf::FieldDescriptor*>;
|
|
using Sub = ::google::protobuf::io::Printer::Sub;
|
|
|
|
void WriteFieldAccessorHazzer(const google::protobuf::Descriptor* desc,
|
|
const google::protobuf::FieldDescriptor* field,
|
|
absl::string_view resolved_field_name,
|
|
absl::string_view resolved_upbc_name,
|
|
Context& ctx);
|
|
void WriteFieldAccessorClear(const google::protobuf::Descriptor* desc,
|
|
const google::protobuf::FieldDescriptor* field,
|
|
absl::string_view resolved_field_name,
|
|
absl::string_view resolved_upbc_name,
|
|
Context& ctx);
|
|
void WriteMapFieldAccessors(const google::protobuf::Descriptor* desc,
|
|
const google::protobuf::FieldDescriptor* field,
|
|
absl::string_view resolved_field_name,
|
|
absl::string_view resolved_upbc_name, Context& ctx);
|
|
|
|
void WriteMapAccessorDefinitions(const google::protobuf::Descriptor* desc,
|
|
const google::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 google::protobuf::FieldDescriptor* field,
|
|
const NameToFieldDescriptorMap& field_names);
|
|
|
|
upb::generator::NameMangler CreateNameMangler(
|
|
const google::protobuf::Descriptor* message) {
|
|
return upb::generator::NameMangler(upb::generator::GetCppFields(message));
|
|
}
|
|
|
|
NameToFieldDescriptorMap CreateFieldNameMap(const google::protobuf::Descriptor* message) {
|
|
NameToFieldDescriptorMap field_names;
|
|
for (int i = 0; i < message->field_count(); i++) {
|
|
const google::protobuf::FieldDescriptor* field = message->field(i);
|
|
field_names.emplace(field->name(), field);
|
|
}
|
|
return field_names;
|
|
}
|
|
|
|
void WriteFieldAccessorsInHeader(const google::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() == google::protobuf::FieldDescriptor::CPPTYPE_STRING) {
|
|
ctx.Emit(
|
|
{Sub("field_name", resolved_field_name)
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
Sub("set_field_name", absl::StrCat("set_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kSet})},
|
|
R"cc(
|
|
absl::string_view $field_name$() const;
|
|
void $set_field_name$(absl::string_view value);
|
|
)cc");
|
|
} else if (field->cpp_type() ==
|
|
google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
|
|
ctx.Emit(
|
|
{{"mut_ptr_type", MessagePtrConstType(field, /* const */ false)},
|
|
{"const_ptr_type", MessagePtrConstType(field, /* const */ true)},
|
|
Sub("field_name", resolved_field_name)
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
Sub("mutable_field_name",
|
|
absl::StrCat("mutable_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kAlias}),
|
|
Sub("set_alias_field_name",
|
|
absl::StrCat("set_alias_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kSet})},
|
|
R"cc(
|
|
$const_ptr_type$ $field_name$() const;
|
|
$mut_ptr_type$ $mutable_field_name$();
|
|
/**
|
|
* Re-points submessage to the given target.
|
|
*
|
|
* REQUIRES:
|
|
* - both messages must be in the same arena, or in two
|
|
* fused arenas.
|
|
*/
|
|
void $set_alias_field_name$($mut_ptr_type$ target);
|
|
)cc");
|
|
} else {
|
|
ctx.Emit(
|
|
{{"cpp_type", CppConstType(field)},
|
|
Sub("field_name", resolved_field_name)
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
Sub("set_field_name", absl::StrCat("set_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kSet}),
|
|
{"upb_msg_name",
|
|
upb::generator::CApiMessageType(desc->full_name())},
|
|
{"upb_field_name", resolved_upbc_name}},
|
|
R"cc(
|
|
inline $cpp_type$ $field_name$() const {
|
|
return $upb_msg_name$_$upb_field_name$(msg_);
|
|
}
|
|
inline void $set_field_name$($cpp_type$ value) {
|
|
return $upb_msg_name$_set_$upb_field_name$(msg_, value);
|
|
}
|
|
)cc");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WriteFieldAccessorHazzer(const google::protobuf::Descriptor* desc,
|
|
const google::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.Emit(
|
|
{Sub("has_field_name", absl::StrCat("has_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"upb_field_name", resolved_upbc_name}},
|
|
R"cc(
|
|
inline bool $has_field_name$() const {
|
|
return $upb_msg_name$_has_$upb_field_name$(msg_);
|
|
}
|
|
)cc");
|
|
}
|
|
}
|
|
|
|
void WriteFieldAccessorClear(const google::protobuf::Descriptor* desc,
|
|
const google::protobuf::FieldDescriptor* field,
|
|
const absl::string_view resolved_field_name,
|
|
const absl::string_view resolved_upbc_name,
|
|
Context& ctx) {
|
|
if (field->has_presence()) {
|
|
ctx.Emit(
|
|
{Sub("clear_field_name", absl::StrCat("clear_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
{"upb_field_name", resolved_upbc_name},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())}},
|
|
R"cc(
|
|
void $clear_field_name$() {
|
|
$upb_msg_name$_clear_$upb_field_name$(msg_);
|
|
}
|
|
)cc");
|
|
}
|
|
}
|
|
|
|
void WriteMapFieldAccessors(const google::protobuf::Descriptor* desc,
|
|
const google::protobuf::FieldDescriptor* field,
|
|
const absl::string_view resolved_field_name,
|
|
const absl::string_view resolved_upbc_name,
|
|
Context& ctx) {
|
|
const google::protobuf::Descriptor* entry = field->message_type();
|
|
const google::protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
|
|
const google::protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
|
|
ctx.Emit(
|
|
{Sub("field_name_size", absl::StrCat(resolved_field_name, "_size"))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
Sub("clear_field_name", absl::StrCat("clear_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
Sub("delete_field_name", absl::StrCat("delete_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"const_key", CppConstType(key)},
|
|
{"upb_field_name", resolved_upbc_name}},
|
|
R"cc(
|
|
inline size_t $field_name_size$() const {
|
|
return $upb_msg_name$_$upb_field_name$_size(msg_);
|
|
}
|
|
inline void $clear_field_name$() {
|
|
$upb_msg_name$_clear_$upb_field_name$(msg_);
|
|
}
|
|
void $delete_field_name$($const_key$ key);
|
|
)cc");
|
|
|
|
if (val->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
|
|
ctx.Emit({Sub("set_field_name", absl::StrCat("set_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kSet}),
|
|
Sub("set_alias_field_name",
|
|
absl::StrCat("set_alias_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kSet}),
|
|
Sub("get_field_name", absl::StrCat("get_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
Sub("get_mutable_field_name",
|
|
absl::StrCat("get_mutable_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kAlias}),
|
|
{"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.Emit({Sub("set_field_name", absl::StrCat("set_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kSet}),
|
|
Sub("get_field_name", absl::StrCat("get_", resolved_field_name))
|
|
.AnnotatedAs({field, io::AnnotationCollector::kNone}),
|
|
{"const_key", CppConstType(key)},
|
|
{"const_val", CppConstType(val)}},
|
|
R"cc(
|
|
bool $set_field_name$($const_key$ key, $const_val$ value);
|
|
absl::StatusOr<$const_val$> $get_field_name$($const_key$ key);
|
|
)cc");
|
|
}
|
|
}
|
|
|
|
void WriteAccessorsInSource(const google::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() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
|
|
WriteRepeatedMessageAccessor(desc, field, resolved_field_name,
|
|
class_name, ctx);
|
|
} else if (field->cpp_type() == google::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() == google::protobuf::FieldDescriptor::CPPTYPE_STRING) {
|
|
ctx.Emit({{"class_name", class_name},
|
|
{"cpp_const_type", CppConstType(field)},
|
|
{"field_name", resolved_field_name},
|
|
{"upb_msg_name",
|
|
upb::generator::CApiMessageType(desc->full_name())},
|
|
{"upb_field_name", resolved_upbc_name}},
|
|
R"cc(
|
|
$cpp_const_type$ $class_name$::$field_name$() const {
|
|
return hpb::interop::upb::FromUpbStringView(
|
|
$upb_msg_name$_$upb_field_name$(msg_));
|
|
}
|
|
)cc");
|
|
// Set string.
|
|
ctx.Emit({{"class_name", class_name},
|
|
{"cpp_const_type", CppConstType(field)},
|
|
{"field_name", resolved_field_name},
|
|
{"upb_field_name", resolved_upbc_name},
|
|
{"upb_msg_name",
|
|
upb::generator::CApiMessageType(desc->full_name())},
|
|
{"arena_expr", arena_expression}},
|
|
R"cc(
|
|
void $class_name$::set_$field_name$($cpp_const_type$ value) {
|
|
$upb_msg_name$_set_$upb_field_name$(
|
|
msg_, hpb::interop::upb::CopyToUpbStringView(
|
|
value, $arena_expr$));
|
|
}
|
|
)cc");
|
|
} else if (field->cpp_type() ==
|
|
google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"const_ptr_type",
|
|
MessagePtrConstType(field, /* is_const */ true)},
|
|
{"field_name", resolved_field_name},
|
|
{"upb_msg_name",
|
|
upb::generator::CApiMessageType(desc->full_name())},
|
|
{"msg_base_type", MessageBaseType(field, /* maybe_const */ false)},
|
|
{"upb_field_name", resolved_upbc_name}},
|
|
R"cc(
|
|
$const_ptr_type$ $class_name$::$field_name$() const {
|
|
if (!has_$field_name$()) {
|
|
return $msg_base_type$::default_instance();
|
|
}
|
|
return ::hpb::interop::upb::MakeCHandle<$msg_base_type$>(
|
|
(upb_Message*)($upb_msg_name$_$upb_field_name$(msg_)),
|
|
arena_);
|
|
}
|
|
)cc");
|
|
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"mut_ptr_type", MessagePtrConstType(field, /* is_const */ false)},
|
|
{"field_name", resolved_field_name},
|
|
{"upb_msg_name",
|
|
upb::generator::CApiMessageType(desc->full_name())},
|
|
{"msg_base_type", MessageBaseType(field, /* maybe_const */ false)},
|
|
{"upb_field_name", resolved_upbc_name},
|
|
{"arena_expr", arena_expression},
|
|
{"desc_class_name", ClassName(desc)},
|
|
{"layout_index", ctx.GetLayoutIndex(field)}},
|
|
R"cc(
|
|
$mut_ptr_type$ $class_name$::mutable_$field_name$() {
|
|
return hpb::interop::upb::MakeHandle<$msg_base_type$>(
|
|
(upb_Message*)($upb_msg_name$_mutable_$upb_field_name$(
|
|
msg_, $arena_expr$)),
|
|
$arena_expr$);
|
|
}
|
|
void $class_name$::set_alias_$field_name$($mut_ptr_type$ target) {
|
|
#ifndef NDEBUG
|
|
ABSL_CHECK(upb_Arena_IsFused(
|
|
arena_, hpb::interop::upb::GetArena(target)) ||
|
|
upb_Arena_HasRef(
|
|
arena_, hpb::interop::upb::GetArena(target)));
|
|
#endif
|
|
upb_Message_SetBaseFieldMessage(
|
|
UPB_UPCAST(msg_),
|
|
upb_MiniTable_GetFieldByIndex(
|
|
$desc_class_name$::minitable(), $layout_index$),
|
|
hpb::interop::upb::GetMessage(target));
|
|
}
|
|
)cc");
|
|
}
|
|
}
|
|
}
|
|
ctx.Emit("\n");
|
|
ctx.Emit("} // namespace internal\n\n");
|
|
}
|
|
|
|
void WriteMapAccessorDefinitions(const google::protobuf::Descriptor* desc,
|
|
const google::protobuf::FieldDescriptor* field,
|
|
const absl::string_view resolved_field_name,
|
|
const absl::string_view class_name,
|
|
Context& ctx) {
|
|
const google::protobuf::Descriptor* entry = field->message_type();
|
|
const google::protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
|
|
const google::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() == google::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() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"const_val_ptr", MessagePtrConstType(val, /* is_const */ true)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"upb_val_msg_name",
|
|
upb::generator::CApiMessageType(val->message_type()->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name},
|
|
{"upb_val_minitable", ::upb::generator::MiniTableMessageVarName(
|
|
val->message_type()->full_name())}},
|
|
R"cc(
|
|
bool $class_name$::set_$field_name$($const_key$ key,
|
|
$const_val_ptr$ value) {
|
|
upb_Message* clone = upb_Message_DeepClone(
|
|
::hpb::internal::PrivateAccess::GetInternalMsg(value),
|
|
&$upb_val_minitable$, arena_);
|
|
$optional_conversion_code$return
|
|
$upb_msg_name$_$upb_field_name$_set(msg_, $converted_key_name$,
|
|
($upb_val_msg_name$*)clone,
|
|
arena_);
|
|
}
|
|
)cc");
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"mut_val_ptr", MessagePtrConstType(val, /* is_const */ false)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"upb_val_msg_name",
|
|
upb::generator::CApiMessageType(val->message_type()->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name},
|
|
{"upb_val_minitable", ::upb::generator::MiniTableMessageVarName(
|
|
val->message_type()->full_name())}},
|
|
R"cc(
|
|
bool $class_name$::set_$field_name$($const_key$ key,
|
|
$mut_val_ptr$ value) {
|
|
upb_Message* clone = upb_Message_DeepClone(
|
|
::hpb::internal::PrivateAccess::GetInternalMsg(value),
|
|
&$upb_val_minitable$, arena_);
|
|
$optional_conversion_code$return
|
|
$upb_msg_name$_$upb_field_name$_set(msg_, $converted_key_name$,
|
|
($upb_val_msg_name$*)clone,
|
|
arena_);
|
|
}
|
|
)cc");
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"const_val_ptr", MessagePtrConstType(val, /* is_const */ true)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"upb_val_msg_name",
|
|
upb::generator::CApiMessageType(val->message_type()->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name}},
|
|
R"cc(
|
|
bool $class_name$::set_alias_$field_name$($const_key$ key,
|
|
$const_val_ptr$ value) {
|
|
#ifndef NDEBUG
|
|
ABSL_CHECK(
|
|
upb_Arena_IsFused(arena_, hpb::interop::upb::GetArena(value)) ||
|
|
upb_Arena_HasRef(arena_, hpb::interop::upb::GetArena(value)));
|
|
#endif
|
|
$optional_conversion_code$return
|
|
$upb_msg_name$_$upb_field_name$_set(
|
|
msg_, $converted_key_name$,
|
|
($upb_val_msg_name$*)hpb::interop::upb::GetMessage(value),
|
|
arena_);
|
|
}
|
|
)cc");
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"mut_val_ptr", MessagePtrConstType(val, /* is_const */ false)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"upb_val_msg_name",
|
|
upb::generator::CApiMessageType(val->message_type()->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name}},
|
|
R"cc(
|
|
bool $class_name$::set_alias_$field_name$($const_key$ key,
|
|
$mut_val_ptr$ value) {
|
|
#ifndef NDEBUG
|
|
ABSL_CHECK(
|
|
|
|
upb_Arena_IsFused(arena_, hpb::interop::upb::GetArena(value)) ||
|
|
upb_Arena_HasRef(arena_, hpb::interop::upb::GetArena(value)));
|
|
#endif
|
|
$optional_conversion_code$return
|
|
$upb_msg_name$_$upb_field_name$_set(
|
|
msg_, $converted_key_name$,
|
|
($upb_val_msg_name$*)hpb::interop::upb::GetMessage(value),
|
|
arena_);
|
|
}
|
|
)cc");
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"const_val_ptr", MessagePtrConstType(val, /* is_const */ true)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"upb_val_msg_name",
|
|
upb::generator::CApiMessageType(val->message_type()->full_name())},
|
|
{"val_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<$const_val_ptr$> $class_name$::get_$field_name$(
|
|
$const_key$ key) {
|
|
$upb_val_msg_name$* 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::MakeCHandle<$val_proto_class$>(
|
|
UPB_UPCAST(msg_value), arena_);
|
|
}
|
|
return absl::NotFoundError("");
|
|
}
|
|
)cc");
|
|
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.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name}},
|
|
R"cc(
|
|
void $class_name$::delete_$field_name$($const_key$ key) {
|
|
$optional_conversion_code$$upb_msg_name$_$upb_field_name$_delete(
|
|
msg_, $converted_key_name$);
|
|
}
|
|
)cc");
|
|
} else if (val->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_STRING) {
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"const_val", CppConstType(val)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name}},
|
|
R"cc(
|
|
bool $class_name$::set_$field_name$($const_key$ key,
|
|
$const_val$ value) {
|
|
$optional_conversion_code$return
|
|
$upb_msg_name$_$upb_field_name$_set(
|
|
msg_, $converted_key_name$,
|
|
hpb::interop::upb::CopyToUpbStringView(value, arena_),
|
|
arena_);
|
|
}
|
|
)cc");
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"const_val", CppConstType(val)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name}},
|
|
R"cc(
|
|
::absl::StatusOr<$const_val$> $class_name$::get_$field_name$(
|
|
$const_key$ key) {
|
|
upb_StringView value;
|
|
$optional_conversion_code$bool success =
|
|
$upb_msg_name$_$upb_field_name$_get(msg_, $converted_key_name$,
|
|
&value);
|
|
if (success) {
|
|
return ::absl::string_view(value.data, value.size);
|
|
}
|
|
return ::absl::NotFoundError("");
|
|
}
|
|
)cc");
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name}},
|
|
R"cc(
|
|
void $class_name$::delete_$field_name$($const_key$ key) {
|
|
$optional_conversion_code$$upb_msg_name$_$upb_field_name$_delete(
|
|
msg_, $converted_key_name$);
|
|
}
|
|
)cc");
|
|
} else {
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"const_val", CppConstType(val)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name}},
|
|
R"cc(
|
|
bool $class_name$::set_$field_name$($const_key$ key,
|
|
$const_val$ value) {
|
|
$optional_conversion_code$return
|
|
$upb_msg_name$_$upb_field_name$_set(msg_, $converted_key_name$,
|
|
value, arena_);
|
|
}
|
|
)cc");
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"const_val", CppConstType(val)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name}},
|
|
R"cc(
|
|
::absl::StatusOr<$const_val$> $class_name$::get_$field_name$(
|
|
$const_key$ key) {
|
|
$const_val$ value;
|
|
$optional_conversion_code$bool success =
|
|
$upb_msg_name$_$upb_field_name$_get(msg_, $converted_key_name$,
|
|
&value);
|
|
if (success) {
|
|
return value;
|
|
}
|
|
return ::absl::NotFoundError("");
|
|
}
|
|
)cc");
|
|
ctx.Emit(
|
|
{{"class_name", class_name},
|
|
{"field_name", resolved_field_name},
|
|
{"const_key", CppConstType(key)},
|
|
{"upb_msg_name", upb::generator::CApiMessageType(desc->full_name())},
|
|
{"optional_conversion_code", optional_conversion_code},
|
|
{"converted_key_name", converted_key_name},
|
|
{"upb_field_name", upbc_name}},
|
|
R"cc(
|
|
void $class_name$::delete_$field_name$($const_key$ key) {
|
|
$optional_conversion_code$$upb_msg_name$_$upb_field_name$_delete(
|
|
msg_, $converted_key_name$);
|
|
}
|
|
)cc");
|
|
}
|
|
}
|
|
|
|
void WriteUsingAccessorsInHeader(const google::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.Emit(
|
|
{{"class_name", class_name}, {"field_name", resolved_field_name}},
|
|
"using $class_name$Access::has_$field_name$;\n");
|
|
if (!read_only) {
|
|
ctx.Emit(
|
|
{{"class_name", class_name}, {"field_name", resolved_field_name}},
|
|
"using $class_name$Access::clear_$field_name$;\n");
|
|
}
|
|
}
|
|
if (field->is_map()) {
|
|
ctx.Emit(
|
|
{{"class_name", class_name}, {"field_name", resolved_field_name}},
|
|
R"cc(
|
|
using $class_name$Access::$field_name$_size;
|
|
using $class_name$Access::get_$field_name$;
|
|
)cc");
|
|
if (!read_only) {
|
|
ctx.Emit(
|
|
{{"class_name", class_name}, {"field_name", resolved_field_name}},
|
|
R"cc(
|
|
using $class_name$Access::clear_$field_name$;
|
|
using $class_name$Access::delete_$field_name$;
|
|
using $class_name$Access::set_$field_name$;
|
|
)cc");
|
|
// only emit set_alias and get_mutable for maps when value is a message
|
|
if (field->message_type()->FindFieldByNumber(2)->cpp_type() ==
|
|
google::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() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
|
|
ctx.Emit({{"class_name", ClassName(desc)},
|
|
{"field_name", resolved_field_name}},
|
|
"using $class_name$Access::$field_name$;\n");
|
|
if (!read_only) {
|
|
ctx.Emit(
|
|
{{"class_name", class_name}, {"field_name", resolved_field_name}},
|
|
"using $class_name$Access::mutable_$field_name$;\n");
|
|
ctx.Emit(
|
|
{{"class_name", class_name}, {"field_name", resolved_field_name}},
|
|
"using $class_name$Access::set_alias_$field_name$;\n");
|
|
}
|
|
} else {
|
|
ctx.Emit(
|
|
{{"class_name", class_name}, {"field_name", resolved_field_name}},
|
|
"using $class_name$Access::$field_name$;\n");
|
|
if (!read_only) {
|
|
ctx.Emit(
|
|
{{"class_name", class_name}, {"field_name", resolved_field_name}},
|
|
"using $class_name$Access::set_$field_name$;\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < desc->real_oneof_decl_count(); ++i) {
|
|
const google::protobuf::OneofDescriptor* oneof = desc->oneof_decl(i);
|
|
ctx.Emit({{"class_name", class_name}, {"oneof_name", oneof->name()}},
|
|
"using $class_name$Access::$oneof_name$_case;\n");
|
|
ctx.Emit({{"class_name", class_name},
|
|
{"name_camel_case",
|
|
ToCamelCase(oneof->name(), /*lower_first=*/false)}},
|
|
"using $class_name$Access::$name_camel_case$Case;\n");
|
|
for (int j = 0; j < oneof->field_count(); ++j) {
|
|
const google::protobuf::FieldDescriptor* field = oneof->field(j);
|
|
ctx.Emit({{"class_name", class_name},
|
|
{"field_camel_case",
|
|
ToCamelCase(field->name(), /*lower_first=*/false)}},
|
|
"using $class_name$Access::k$field_camel_case$;\n");
|
|
}
|
|
ctx.Emit({{"class_name", class_name},
|
|
{"oneof_upper", absl::AsciiStrToUpper(oneof->name())}},
|
|
"using $class_name$Access::$oneof_upper$_NOT_SET;\n");
|
|
}
|
|
}
|
|
|
|
void WriteOneofAccessorsInHeader(const google::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 google::protobuf::OneofDescriptor* oneof = desc->oneof_decl(i);
|
|
ctx.Emit({{"name_camel_case",
|
|
ToCamelCase(oneof->name(), /*lower_first=*/false)}},
|
|
"enum $name_camel_case$Case {\n");
|
|
for (int j = 0; j < oneof->field_count(); ++j) {
|
|
const google::protobuf::FieldDescriptor* field = oneof->field(j);
|
|
ctx.Emit({{"field_camel_case",
|
|
ToCamelCase(field->name(), /*lower_first=*/false)},
|
|
{"field_number", field->number()}},
|
|
" k$field_camel_case$ = $field_number$,\n");
|
|
}
|
|
ctx.Emit({{"oneof_upper", absl::AsciiStrToUpper(oneof->name())}},
|
|
" $oneof_upper$_NOT_SET = 0,\n");
|
|
ctx.Emit("};\n\n");
|
|
ctx.Emit(
|
|
{{"name_camel_case", ToCamelCase(oneof->name(), /*lower_first=*/false)},
|
|
{"oneof_name", oneof->name()}},
|
|
"$name_camel_case$Case $oneof_name$_case() const {\n");
|
|
for (int j = 0; j < oneof->field_count(); ++j) {
|
|
const google::protobuf::FieldDescriptor* field = oneof->field(j);
|
|
std::string resolved_field_name = ResolveFieldName(field, field_names);
|
|
ctx.Emit({{"field_name", resolved_field_name},
|
|
{"field_camel_case",
|
|
ToCamelCase(field->name(), /*lower_first=*/false)}},
|
|
" if (has_$field_name$()) { return k$field_camel_case$; }\n");
|
|
}
|
|
ctx.Emit({{"oneof_upper", absl::AsciiStrToUpper(oneof->name())}},
|
|
" return $oneof_upper$_NOT_SET;\n");
|
|
ctx.Emit("}\n;");
|
|
}
|
|
}
|
|
|
|
std::string ResolveFieldName(const google::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() == google::protobuf::FieldDescriptor::CPPTYPE_STRING &&
|
|
prefix == kClearMethodPrefix) ||
|
|
prefix == kSetMethodPrefix || prefix == kHasMethodPrefix) {
|
|
return absl::StrCat(field_name, "_");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ResolveKeywordConflict(field_name);
|
|
}
|
|
|
|
} // namespace hpb_generator
|
|
} // namespace protobuf
|
|
} // namespace google
|