|
|
|
@ -11,9 +11,12 @@ |
|
|
|
|
|
|
|
|
|
#include "google/protobuf/compiler/cpp/extension.h" |
|
|
|
|
|
|
|
|
|
#include <string> |
|
|
|
|
|
|
|
|
|
#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" |
|
|
|
@ -77,97 +80,134 @@ ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor, |
|
|
|
|
variables_["scope"] = scope; |
|
|
|
|
variables_["scoped_name"] = ExtensionName(descriptor_); |
|
|
|
|
variables_["number"] = absl::StrCat(descriptor_->number()); |
|
|
|
|
|
|
|
|
|
bool add_verify_fn = |
|
|
|
|
// 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_); |
|
|
|
|
|
|
|
|
|
variables_["verify_fn"] = |
|
|
|
|
add_verify_fn |
|
|
|
|
? absl::StrCat("&", FieldMessageTypeName(descriptor_, options_), |
|
|
|
|
"::InternalVerify") |
|
|
|
|
: "nullptr"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ExtensionGenerator::~ExtensionGenerator() {} |
|
|
|
|
ExtensionGenerator::~ExtensionGenerator() = default; |
|
|
|
|
|
|
|
|
|
bool ExtensionGenerator::IsScoped() const { |
|
|
|
|
return descriptor_->extension_scope() != nullptr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void ExtensionGenerator::GenerateDeclaration(io::Printer* printer) const { |
|
|
|
|
Formatter format(printer, variables_); |
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
std::string qualifier; |
|
|
|
|
if (!IsScoped()) { |
|
|
|
|
qualifier = "extern"; |
|
|
|
|
if (!options_.dllexport_decl.empty()) { |
|
|
|
|
qualifier = absl::StrCat(options_.dllexport_decl, " ", qualifier); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
qualifier = "static"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
format( |
|
|
|
|
"static const int $constant_name$ = $number$;\n" |
|
|
|
|
"$1$ ::$proto_ns$::internal::ExtensionIdentifier< $extendee$,\n" |
|
|
|
|
" ::$proto_ns$::internal::$type_traits$, $field_type$, $packed$ >\n" |
|
|
|
|
" ${2$$name$$}$;\n", |
|
|
|
|
qualifier, descriptor_); |
|
|
|
|
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* printer) { |
|
|
|
|
Formatter format(printer, variables_); |
|
|
|
|
std::string default_str; |
|
|
|
|
// If this is a class member, it needs to be declared in its class scope.
|
|
|
|
|
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.
|
|
|
|
|
default_str = |
|
|
|
|
absl::StrReplaceAll(variables_["scoped_name"], {{"::", "_"}}) + |
|
|
|
|
// 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"; |
|
|
|
|
format("const std::string $1$($2$);\n", default_str, |
|
|
|
|
DefaultValue(options_, descriptor_)); |
|
|
|
|
} else if (descriptor_->message_type()) { |
|
|
|
|
// We have to initialize the default instance for extensions at registration
|
|
|
|
|
// time.
|
|
|
|
|
default_str = absl::StrCat(FieldMessageTypeName(descriptor_, options_), |
|
|
|
|
// We have to initialize the default instance for extensions at
|
|
|
|
|
// registration time.
|
|
|
|
|
return absl::StrCat(FieldMessageTypeName(descriptor_, options_), |
|
|
|
|
"::default_instance()"); |
|
|
|
|
} else { |
|
|
|
|
default_str = DefaultValue(options_, descriptor_); |
|
|
|
|
return DefaultValue(options_, descriptor_); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Likewise, class members need to declare the field constant variable.
|
|
|
|
|
if (IsScoped()) { |
|
|
|
|
format( |
|
|
|
|
"#if !defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)\n" |
|
|
|
|
"const int $scope$$constant_name$;\n" |
|
|
|
|
"#endif\n"); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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", |
|
|
|
|
[&] { |
|
|
|
|
if (IsLazilyInitializedFile(descriptor_->file()->name())) { |
|
|
|
|
p->Emit(R"cc( |
|
|
|
|
PROTOBUF_CONSTINIT$ dllexport_decl$ |
|
|
|
|
PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 ::$proto_ns$::internal:: |
|
|
|
|
ExtensionIdentifier< |
|
|
|
|
$extendee$, ::$proto_ns$::internal::$type_traits$, |
|
|
|
|
$field_type$, $packed$> |
|
|
|
|
$scoped_name$($constant_name$); |
|
|
|
|
)cc"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (IsLazilyInitializedFile(descriptor_->file()->name())) { |
|
|
|
|
format( |
|
|
|
|
"PROTOBUF_CONSTINIT$ dllexport_decl$ " |
|
|
|
|
"PROTOBUF_ATTRIBUTE_INIT_PRIORITY2\n" |
|
|
|
|
"::$proto_ns$::internal::ExtensionIdentifier< $extendee$,\n" |
|
|
|
|
" ::$proto_ns$::internal::$type_traits$, $field_type$, $packed$>\n" |
|
|
|
|
" $scoped_name$($constant_name$);\n"); |
|
|
|
|
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_); |
|
|
|
|
|
|
|
|
|
if (should_verify) { |
|
|
|
|
p->Emit(R"cc( |
|
|
|
|
$dllexport_decl $PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 :: |
|
|
|
|
$proto_ns$::internal::ExtensionIdentifier< |
|
|
|
|
$extendee$, ::$proto_ns$::internal::$type_traits$, |
|
|
|
|
$field_type$, $packed$> |
|
|
|
|
$scoped_name$($constant_name$, $default_str$, |
|
|
|
|
&$message_type$::InternalVerify); |
|
|
|
|
)cc"); |
|
|
|
|
} else { |
|
|
|
|
format( |
|
|
|
|
"$dllexport_decl $PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 " |
|
|
|
|
"::$proto_ns$::internal::ExtensionIdentifier< $extendee$,\n" |
|
|
|
|
" ::$proto_ns$::internal::$type_traits$, $field_type$, $packed$>\n" |
|
|
|
|
" $scoped_name$($constant_name$, $1$, $verify_fn$);\n", |
|
|
|
|
default_str); |
|
|
|
|
p->Emit(R"cc( |
|
|
|
|
$dllexport_decl $PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 :: |
|
|
|
|
$proto_ns$::internal::ExtensionIdentifier< |
|
|
|
|
$extendee$, ::$proto_ns$::internal::$type_traits$, |
|
|
|
|
$field_type$, $packed$> |
|
|
|
|
$scoped_name$($constant_name$, $default_str$, nullptr); |
|
|
|
|
)cc"); |
|
|
|
|
} |
|
|
|
|
}}, |
|
|
|
|
}, |
|
|
|
|
R"cc( |
|
|
|
|
$declare_default_str$; |
|
|
|
|
$declare_const_var$; |
|
|
|
|
$define_extension_id$; |
|
|
|
|
)cc"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace cpp
|
|
|
|
|