| // 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 "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*>; |
| |
| 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({{"field_name", resolved_field_name}}, |
| 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)}, |
| {"field_name", resolved_field_name}}, |
| 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)}, |
| {"field_name", resolved_field_name}, |
| {"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( |
| {{"field_name", resolved_field_name}, |
| {"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( |
| {{"field_name", resolved_field_name}, |
| {"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( |
| {{"field_name", resolved_field_name}, |
| {"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({{"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.Emit({{"field_name", resolved_field_name}, |
| {"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) { |
| $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) { |
| $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 |