| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. 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 |
| |
| // Author: kenton@google.com (Kenton Varda) |
| // Based on original Protocol Buffers design by |
| // Sanjay Ghemawat, Jeff Dean, and others. |
| |
| #include "google/protobuf/compiler/cpp/extension.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "absl/log/absl_check.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_replace.h" |
| #include "google/protobuf/compiler/cpp/helpers.h" |
| #include "google/protobuf/compiler/cpp/options.h" |
| #include "google/protobuf/descriptor.h" |
| #include "google/protobuf/descriptor.pb.h" |
| #include "google/protobuf/io/printer.h" |
| |
| namespace google { |
| namespace protobuf { |
| namespace compiler { |
| namespace cpp { |
| |
| ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor, |
| const Options& options, |
| MessageSCCAnalyzer* scc_analyzer) |
| : descriptor_(descriptor), options_(options), scc_analyzer_(scc_analyzer) { |
| // Construct type_traits_. |
| if (descriptor_->is_repeated()) { |
| type_traits_ = "Repeated"; |
| } |
| |
| switch (descriptor_->cpp_type()) { |
| case FieldDescriptor::CPPTYPE_ENUM: |
| type_traits_.append("EnumTypeTraits< "); |
| type_traits_.append(ClassName(descriptor_->enum_type(), true)); |
| type_traits_.append(", "); |
| type_traits_.append(ClassName(descriptor_->enum_type(), true)); |
| type_traits_.append("_IsValid>"); |
| break; |
| case FieldDescriptor::CPPTYPE_STRING: |
| type_traits_.append("StringTypeTraits"); |
| break; |
| case FieldDescriptor::CPPTYPE_MESSAGE: |
| type_traits_.append("MessageTypeTraits< "); |
| type_traits_.append(ClassName(descriptor_->message_type(), true)); |
| type_traits_.append(" >"); |
| break; |
| default: |
| type_traits_.append("PrimitiveTypeTraits< "); |
| type_traits_.append(PrimitiveTypeName(options_, descriptor_->cpp_type())); |
| type_traits_.append(" >"); |
| break; |
| } |
| SetCommonMessageDataVariables(descriptor_->containing_type(), &variables_); |
| variables_["extendee"] = |
| QualifiedClassName(descriptor_->containing_type(), options_); |
| variables_["type_traits"] = type_traits_; |
| variables_["name"] = ResolveKeyword(descriptor_->name()); |
| variables_["constant_name"] = FieldConstantName(descriptor_); |
| variables_["field_type"] = |
| absl::StrCat(static_cast<int>(descriptor_->type())); |
| variables_["repeated"] = descriptor_->is_repeated() ? "true" : "false"; |
| variables_["packed"] = descriptor_->is_packed() ? "true" : "false"; |
| variables_["dllexport_decl"] = options.dllexport_decl; |
| |
| std::string scope; |
| if (IsScoped()) { |
| scope = |
| absl::StrCat(ClassName(descriptor_->extension_scope(), false), "::"); |
| } |
| |
| variables_["scope"] = scope; |
| variables_["scoped_name"] = ExtensionName(descriptor_); |
| variables_["number"] = absl::StrCat(descriptor_->number()); |
| } |
| |
| ExtensionGenerator::~ExtensionGenerator() = default; |
| |
| bool ExtensionGenerator::IsScoped() const { |
| return descriptor_->extension_scope() != nullptr; |
| } |
| |
| void ExtensionGenerator::GenerateDeclaration(io::Printer* p) const { |
| auto var = p->WithVars(variables_); |
| auto annotate = p->WithAnnotations({{"name", descriptor_}}); |
| |
| p->Emit({{"qualifier", |
| // If this is a class member, it needs to be declared "static". |
| // Otherwise, it needs to be "extern". In the latter case, it |
| // also needs the DLL export/import specifier. |
| IsScoped() ? "static" |
| : options_.dllexport_decl.empty() |
| ? "extern" |
| : absl::StrCat(options_.dllexport_decl, " extern")}}, |
| R"cc( |
| static const int $constant_name$ = $number$; |
| $qualifier$ ::$proto_ns$::internal::ExtensionIdentifier< |
| $extendee$, ::$proto_ns$::internal::$type_traits$, $field_type$, |
| $packed$> |
| $name$; |
| )cc"); |
| } |
| |
| void ExtensionGenerator::GenerateDefinition(io::Printer* p) { |
| auto vars = p->WithVars(variables_); |
| auto generate_default_string = [&] { |
| if (descriptor_->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { |
| // We need to declare a global string which will contain the default |
| // value. We cannot declare it at class scope because that would require |
| // exposing it in the header which would be annoying for other reasons. So |
| // we replace :: with _ in the name and declare it as a global. |
| return absl::StrReplaceAll(variables_["scoped_name"], {{"::", "_"}}) + |
| "_default"; |
| } else if (descriptor_->message_type()) { |
| // We have to initialize the default instance for extensions at |
| // registration time. |
| return absl::StrCat("&", QualifiedDefaultInstanceName( |
| descriptor_->message_type(), options_)); |
| } else { |
| return DefaultValue(options_, descriptor_); |
| } |
| }; |
| |
| auto local_var = p->WithVars({ |
| {"default_str", generate_default_string()}, |
| {"default_val", DefaultValue(options_, descriptor_)}, |
| {"message_type", descriptor_->message_type() != nullptr |
| ? FieldMessageTypeName(descriptor_, options_) |
| : ""}, |
| }); |
| p->Emit( |
| { |
| {"declare_default_str", |
| [&] { |
| if (descriptor_->cpp_type() != FieldDescriptor::CPPTYPE_STRING) |
| return; |
| |
| // If this is a class member, it needs to be declared in its class |
| // scope. |
| p->Emit(R"cc( |
| const std::string $default_str$($default_val$); |
| )cc"); |
| }}, |
| {"declare_const_var", |
| [&] { |
| if (!IsScoped()) return; |
| // Likewise, class members need to declare the field constant |
| // variable. |
| p->Emit(R"cc( |
| #if !defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912) |
| const int $scope$$constant_name$; |
| #endif |
| )cc"); |
| }}, |
| {"define_extension_id", |
| [&] { |
| p->Emit(R"cc( |
| PROTOBUF_CONSTINIT$ dllexport_decl$ |
| PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 ::_pbi:: |
| ExtensionIdentifier<$extendee$, ::_pbi::$type_traits$, |
| $field_type$, $packed$> |
| $scoped_name$($constant_name$, $default_str$); |
| )cc"); |
| }}, |
| }, |
| R"cc( |
| $declare_default_str$; |
| $declare_const_var$; |
| $define_extension_id$; |
| )cc"); |
| } |
| |
| bool ExtensionGenerator::WillGenerateRegistration(InitPriority priority) { |
| // When not using weak descriptors we initialize everything on priority 102. |
| if (!UsingImplicitWeakDescriptor(descriptor_->file(), options_)) { |
| return priority == kInitPriority102; |
| } |
| return true; |
| } |
| |
| void ExtensionGenerator::GenerateRegistration(io::Printer* p, |
| InitPriority priority) { |
| ABSL_CHECK(WillGenerateRegistration(priority)); |
| const bool using_implicit_weak_descriptors = |
| UsingImplicitWeakDescriptor(descriptor_->file(), options_); |
| const auto find_index = [](auto* desc) { |
| const std::vector<const Descriptor*> msgs = |
| FlattenMessagesInFile(desc->file()); |
| return absl::c_find(msgs, desc) - msgs.begin(); |
| }; |
| auto vars = p->WithVars(variables_); |
| auto vars2 = p->WithVars({{ |
| {"extendee_table", |
| DescriptorTableName(descriptor_->containing_type()->file(), options_)}, |
| {"extendee_index", find_index(descriptor_->containing_type())}, |
| {"preregister", priority == kInitPriority101}, |
| }}); |
| switch (descriptor_->cpp_type()) { |
| case FieldDescriptor::CPPTYPE_ENUM: |
| if (using_implicit_weak_descriptors) { |
| p->Emit({{"enum_name", ClassName(descriptor_->enum_type(), true)}}, |
| R"cc( |
| (::_pbi::ExtensionSet::ShouldRegisterAtThisTime( |
| {{&$extendee_table$, $extendee_index$}}, $preregister$) |
| ? ::_pbi::ExtensionSet::RegisterEnumExtension( |
| ::_pbi::GetPrototypeForWeakDescriptor( |
| &$extendee_table$, $extendee_index$, true), |
| $number$, $field_type$, $repeated$, $packed$, |
| $enum_name$_IsValid) |
| : (void)0), |
| )cc"); |
| } else if (priority == kInitPriority102) { |
| p->Emit({{"enum_name", ClassName(descriptor_->enum_type(), true)}}, |
| R"cc( |
| ::_pbi::ExtensionSet::RegisterEnumExtension( |
| &$extendee$::default_instance(), $number$, $field_type$, |
| $repeated$, $packed$, $enum_name$_IsValid), |
| )cc"); |
| } |
| |
| break; |
| case FieldDescriptor::CPPTYPE_MESSAGE: { |
| const bool should_verify = |
| // Only verify msgs. |
| descriptor_->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && |
| // Options say to verify. |
| ShouldVerify(descriptor_->message_type(), options_, scc_analyzer_) && |
| ShouldVerify(descriptor_->containing_type(), options_, scc_analyzer_); |
| const auto message_type = FieldMessageTypeName(descriptor_, options_); |
| auto v = p->WithVars( |
| {{"verify", should_verify |
| ? absl::StrCat("&", message_type, "::InternalVerify") |
| : "nullptr"}, |
| {"message_type", message_type}, |
| {"lazy", descriptor_->options().has_lazy() |
| ? descriptor_->options().lazy() ? "kLazy" : "kEager" |
| : "kUndefined"}}); |
| if (using_implicit_weak_descriptors) { |
| p->Emit( |
| { |
| {"extension_table", |
| DescriptorTableName(descriptor_->message_type()->file(), |
| options_)}, |
| {"extension_index", find_index(descriptor_->message_type())}, |
| }, |
| R"cc( |
| (::_pbi::ExtensionSet::ShouldRegisterAtThisTime( |
| {{&$extendee_table$, $extendee_index$}, |
| {&$extension_table$, $extension_index$}}, |
| $preregister$) |
| ? ::_pbi::ExtensionSet::RegisterMessageExtension( |
| ::_pbi::GetPrototypeForWeakDescriptor( |
| &$extendee_table$, $extendee_index$, true), |
| $number$, $field_type$, $repeated$, $packed$, |
| ::_pbi::GetPrototypeForWeakDescriptor( |
| &$extension_table$, $extension_index$, true), |
| $verify$, ::_pbi::LazyAnnotation::$lazy$) |
| : (void)0), |
| )cc"); |
| } else if (priority == kInitPriority102) { |
| p->Emit(R"cc( |
| ::_pbi::ExtensionSet::RegisterMessageExtension( |
| &$extendee$::default_instance(), $number$, $field_type$, |
| $repeated$, $packed$, &$message_type$::default_instance(), |
| $verify$, ::_pbi::LazyAnnotation::$lazy$), |
| )cc"); |
| } |
| break; |
| } |
| |
| default: |
| if (using_implicit_weak_descriptors) { |
| p->Emit(R"cc( |
| (::_pbi::ExtensionSet::ShouldRegisterAtThisTime( |
| {{&$extendee_table$, $extendee_index$}}, $preregister$) |
| ? ::_pbi::ExtensionSet::RegisterExtension( |
| ::_pbi::GetPrototypeForWeakDescriptor(&$extendee_table$, |
| $extendee_index$, |
| true), |
| $number$, $field_type$, $repeated$, $packed$) |
| : (void)0), |
| )cc"); |
| } else if (priority == kInitPriority102) { |
| p->Emit( |
| R"cc( |
| ::_pbi::ExtensionSet::RegisterExtension( |
| &$extendee$::default_instance(), $number$, $field_type$, |
| $repeated$, $packed$), |
| )cc"); |
| } |
| |
| break; |
| } |
| } |
| |
| } // namespace cpp |
| } // namespace compiler |
| } // namespace protobuf |
| } // namespace google |