[ObjC] Convert GenerateSource over to Printer::Emit.

PiperOrigin-RevId: 492228634
pull/11116/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent 10b8034be6
commit c928bf3f25
  1. 562
      src/google/protobuf/compiler/objectivec/file.cc
  2. 16
      src/google/protobuf/compiler/objectivec/file.h
  3. 24
      src/google/protobuf/compiler/objectivec/import_writer.cc
  4. 5
      src/google/protobuf/compiler/objectivec/import_writer.h

@ -43,7 +43,6 @@
#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "google/protobuf/compiler/objectivec/enum.h"
#include "google/protobuf/compiler/objectivec/extension.h"
@ -67,10 +66,6 @@ const int32_t GOOGLE_PROTOBUF_OBJC_VERSION = 30005;
const char* kHeaderExtension = ".pbobjc.h";
std::string BundledFileName(const FileDescriptor* file) {
return "GPB" + FilePathBasename(file) + kHeaderExtension;
}
// Checks if a message contains any extension definitions (on the message or
// a nested message under it).
bool MessageContainsExtensions(const Descriptor* message) {
@ -412,20 +407,17 @@ void FileGenerator::GenerateHeader(io::Printer* printer) const {
}
void FileGenerator::GenerateSource(io::Printer* printer) const {
// #import the runtime support.
std::vector<std::string> headers;
headers.push_back("GPBProtocolBuffers_RuntimeSupport.h");
if (is_bundled_proto_) {
headers.push_back(BundledFileName(file_));
}
PrintFileRuntimePreamble(printer, headers);
ImportWriter import_writer(
generation_options_.generate_for_named_framework,
generation_options_.named_framework_to_proto_path_mappings_path,
generation_options_.runtime_import_prefix,
/* for_bundled_proto = */ is_bundled_proto_);
const std::string header_extension(kHeaderExtension);
// Enums use atomic in the generated code, so add the system import as needed.
if (!enum_generators_.empty()) {
printer->Print(
"#import <stdatomic.h>\n"
"\n");
}
// #import the runtime support.
import_writer.AddRuntimeImport("GPBProtocolBuffers_RuntimeSupport.h");
// #import the header for this proto file.
import_writer.AddFile(file_, header_extension);
std::vector<const FileDescriptor*> deps_with_extensions =
common_state_->CollectMinimalFileDepsContainingExtensions(file_);
@ -435,52 +427,30 @@ void FileGenerator::GenerateSource(io::Printer* printer) const {
generation_options_.headers_use_forward_declarations &&
!is_bundled_proto_;
{
ImportWriter import_writer(
generation_options_.generate_for_named_framework,
generation_options_.named_framework_to_proto_path_mappings_path,
generation_options_.runtime_import_prefix,
/* include_wkt_imports = */ false);
const std::string header_extension(kHeaderExtension);
// #import the header for this proto file.
import_writer.AddFile(file_, header_extension);
if (headers_use_forward_declarations) {
// #import the headers for anything that a plain dependency of this proto
// file (that means they were just an include, not a "public" include).
absl::flat_hash_set<std::string> public_import_names;
for (int i = 0; i < file_->public_dependency_count(); i++) {
public_import_names.insert(file_->public_dependency(i)->name());
}
for (int i = 0; i < file_->dependency_count(); i++) {
const FileDescriptor* dep = file_->dependency(i);
if (!public_import_names.contains(dep->name())) {
import_writer.AddFile(dep, header_extension);
}
}
if (headers_use_forward_declarations) {
// #import the headers for anything that a plain dependency of this proto
// file (that means they were just an include, not a "public" include).
absl::flat_hash_set<std::string> public_import_names;
for (int i = 0; i < file_->public_dependency_count(); i++) {
public_import_names.insert(file_->public_dependency(i)->name());
}
// If any indirect dependency provided extensions, it needs to be directly
// imported so it can get merged into the root's extensions registry.
// See the Note by CollectMinimalFileDepsContainingExtensions before
// changing this.
for (std::vector<const FileDescriptor*>::iterator iter =
deps_with_extensions.begin();
iter != deps_with_extensions.end(); ++iter) {
if (!IsDirectDependency(*iter, file_)) {
import_writer.AddFile(*iter, header_extension);
for (int i = 0; i < file_->dependency_count(); i++) {
const FileDescriptor* dep = file_->dependency(i);
if (!public_import_names.contains(dep->name())) {
import_writer.AddFile(dep, header_extension);
}
}
import_writer.EmitFileImports(printer);
}
bool includes_oneof = false;
for (const auto& generator : message_generators_) {
if (generator->IncludesOneOfDefinition()) {
includes_oneof = true;
break;
// If any indirect dependency provided extensions, it needs to be directly
// imported so it can get merged into the root's extensions registry.
// See the Note by CollectMinimalFileDepsContainingExtensions before
// changing this.
for (std::vector<const FileDescriptor*>::iterator iter =
deps_with_extensions.begin();
iter != deps_with_extensions.end(); ++iter) {
if (!IsDirectDependency(*iter, file_)) {
import_writer.AddFile(*iter, header_extension);
}
}
@ -492,249 +462,261 @@ void FileGenerator::GenerateSource(io::Printer* printer) const {
generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
}
// Note:
// deprecated-declarations suppression is only needed if some place in this
// proto file is something deprecated or if it references something from
// another file that is deprecated.
// dollar-in-identifier-extension is needed because we use references to
// objc class names that have $ in identifiers.
// clang-format off
printer->Print(
"// @@protoc_insertion_point(imports)\n"
"\n"
"#pragma clang diagnostic push\n"
"#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n");
// clang-format on
if (includes_oneof) {
// The generated code for oneof's uses direct ivar access, suppress the
// warning in case developer turn that on in the context they compile the
// generated code.
printer->Print(
"#pragma clang diagnostic ignored \"-Wdirect-ivar-access\"\n");
}
if (!fwd_decls.empty()) {
// clang-format off
printer->Print(
"#pragma clang diagnostic ignored \"-Wdollar-in-identifier-extension\"\n");
// clang-format on
}
printer->Print("\n");
if (!fwd_decls.empty()) {
// clang-format off
printer->Print(
"#pragma mark - Objective C Class declarations\n"
"// Forward declarations of Objective C classes that we can use as\n"
"// static values in struct initializers.\n"
"// We don't use [Foo class] because it is not a static value.\n");
// clang-format on
}
for (const auto& i : fwd_decls) {
printer->Print("$value$\n", "value", i);
}
if (!fwd_decls.empty()) {
printer->Print("\n");
}
printer->Print(
// clang-format off
"#pragma mark - $root_class_name$\n"
"\n"
"@implementation $root_class_name$\n\n",
// clang-format on
"root_class_name", root_class_name_);
const bool file_contains_extensions = !extension_generators_.empty();
// If there were any extensions or this file has any dependencies, output
// a registry to override to create the file specific registry.
if (file_contains_extensions || !deps_with_extensions.empty()) {
// clang-format off
printer->Print(
"+ (GPBExtensionRegistry*)extensionRegistry {\n"
" // This is called by +initialize so there is no need to worry\n"
" // about thread safety and initialization of registry.\n"
" static GPBExtensionRegistry* registry = nil;\n"
" if (!registry) {\n"
" GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n"
" registry = [[GPBExtensionRegistry alloc] init];\n");
// clang-format on
printer->Indent();
printer->Indent();
if (file_contains_extensions) {
printer->Print("static GPBExtensionDescription descriptions[] = {\n");
printer->Indent();
for (const auto& generator : extension_generators_) {
generator->GenerateStaticVariablesInitialization(printer);
}
printer->Outdent();
// clang-format off
printer->Print(
"};\n"
"for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n"
" GPBExtensionDescriptor *extension =\n"
" [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]\n"
" usesClassRefs:YES];\n"
" [registry addExtension:extension];\n"
" [self globallyRegisterExtension:extension];\n"
" [extension release];\n"
"}\n");
// clang-format on
}
printer->Emit(
{
// Avoid the directive within the string below being seen by the
// tool.
{"clangfmt", "clang-format"},
{"filename", file_->name()},
{"root_class_name", root_class_name_},
{"runtime_imports",
[&] {
import_writer.EmitRuntimeImports(
printer, /* default_cpp_symbol = */ !is_bundled_proto_);
}},
{"file_imports", [&] { import_writer.EmitFileImports(printer); }},
{"extra_imports",
[&] {
// Enums use atomic in the generated code, so add the system import
// as needed.
if (!enum_generators_.empty()) {
printer->Emit("#import <stdatomic.h>\n\n");
}
}},
{"extra_pragmas",
[&] {
// The generated code for oneof's uses direct ivar access, suppress
// the warning in case developer turn that on in the context they
// compile the generated code.
for (const auto& generator : message_generators_) {
if (generator->IncludesOneOfDefinition()) {
printer->Emit(R"objc(
#pragma clang diagnostic ignored "-Wdirect-ivar-access"
)objc");
break;
}
}
if (!fwd_decls.empty()) {
printer->Emit(R"objc(
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
)objc");
}
}},
{"fwd_decls",
[&] {
if (!fwd_decls.empty()) {
printer->Emit({{"decls", absl::StrJoin(fwd_decls, "\n")}},
R"objc(
#pragma mark - Objective C Class declarations
// Forward declarations of Objective C classes that we can use as
// static values in struct initializers.
// We don't use [Foo class] because it is not a static value.
$decls$
if (deps_with_extensions.empty()) {
// clang-format off
printer->Print(
"// None of the imports (direct or indirect) defined extensions, so no need to add\n"
"// them to this registry.\n");
// clang-format on
} else {
// clang-format off
printer->Print(
"// Merge in the imports (direct or indirect) that defined extensions.\n");
// clang-format on
for (std::vector<const FileDescriptor*>::iterator iter =
deps_with_extensions.begin();
iter != deps_with_extensions.end(); ++iter) {
const std::string root_class_name(FileClassName((*iter)));
printer->Print(
"[registry addExtensions:[$dependency$ extensionRegistry]];\n",
"dependency", root_class_name);
}
}
)objc");
}
}},
{"root_implementation",
[&] { EmitRootImplementation(printer, deps_with_extensions); }},
{"file_descriptor_implementation",
[&] { EmitFileDescriptorImplementation(printer); }},
{"enums_and_messages",
[&] {
for (const auto& generator : enum_generators_) {
generator->GenerateSource(printer);
}
for (const auto& generator : message_generators_) {
generator->GenerateSource(printer);
}
}},
},
R"objc(
// Generated by the protocol buffer compiler. DO NOT EDIT!
// $clangfmt$ off
// source: $filename$
printer->Outdent();
printer->Outdent();
$runtime_imports$
// clang-format off
printer->Print(
" }\n"
" return registry;\n"
"}\n");
// clang-format on
} else {
if (file_->dependency_count() > 0) {
// clang-format off
printer->Print(
"// No extensions in the file and none of the imports (direct or indirect)\n"
"// defined extensions, so no need to generate +extensionRegistry.\n");
// clang-format on
} else {
// clang-format off
printer->Print(
"// No extensions in the file and no imports, so no need to generate\n"
"// +extensionRegistry.\n");
// clang-format on
}
}
$extra_imports$;
$file_imports$
// @@protoc_insertion_point(imports)
printer->Print("\n@end\n\n");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
$extra_pragmas$
// File descriptor only needed if there are messages to use it.
if (!message_generators_.empty()) {
absl::flat_hash_map<absl::string_view, std::string> vars;
vars["root_class_name"] = root_class_name_;
vars["package"] = file_->package();
vars["objc_prefix"] = FileClassPrefix(file_);
switch (file_->syntax()) {
case FileDescriptor::SYNTAX_UNKNOWN:
vars["syntax"] = "GPBFileSyntaxUnknown";
break;
case FileDescriptor::SYNTAX_PROTO2:
vars["syntax"] = "GPBFileSyntaxProto2";
break;
case FileDescriptor::SYNTAX_PROTO3:
vars["syntax"] = "GPBFileSyntaxProto3";
break;
}
// clang-format off
printer->Print(
vars,
"#pragma mark - $root_class_name$_FileDescriptor\n"
"\n"
"static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n"
" // This is called by +initialize so there is no need to worry\n"
" // about thread safety of the singleton.\n"
" static GPBFileDescriptor *descriptor = NULL;\n"
" if (!descriptor) {\n"
" GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n");
// clang-format on
if (!vars["objc_prefix"].empty()) {
// clang-format off
printer->Print(
vars,
" descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
" objcPrefix:@\"$objc_prefix$\"\n"
" syntax:$syntax$];\n");
// clang-format on
} else {
// clang-format off
printer->Print(
vars,
" descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
" syntax:$syntax$];\n");
// clang-format on
}
// clang-format off
printer->Print(
" }\n"
" return descriptor;\n"
"}\n"
"\n");
// clang-format on
}
$fwd_decls$;
$root_implementation$
for (const auto& generator : enum_generators_) {
generator->GenerateSource(printer);
}
for (const auto& generator : message_generators_) {
generator->GenerateSource(printer);
}
$file_descriptor_implementation$;
$enums_and_messages$;
#pragma clang diagnostic pop
// clang-format off
printer->Print(
"\n"
"#pragma clang diagnostic pop\n"
"\n"
"// @@protoc_insertion_point(global_scope)\n"
"\n"
"// clang-format on\n");
// clang-format on
// @@protoc_insertion_point(global_scope)
// $clangfmt$ on
)objc");
}
// Helper to print the import of the runtime support at the top of generated
// files. This currently only supports the runtime coming from a framework
// as defined by the official CocoaPod.
void FileGenerator::PrintFileRuntimePreamble(
io::Printer* printer,
const std::vector<std::string>& headers_to_import) const {
printer->Print(
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
"// clang-format off\n"
"// source: $filename$\n"
"\n",
"filename", file_->name());
void FileGenerator::EmitRootImplementation(
io::Printer* p,
const std::vector<const FileDescriptor*>& deps_with_extensions) const {
p->Emit(
{
{"root_class_name", root_class_name_},
{"root_extension_registry",
[&] {
// If there were any extensions or this file has any dependencies,
// output a registry to override to create the file specific
// registry.
if (!!extension_generators_.empty() &&
deps_with_extensions.empty()) {
if (file_->dependency_count() == 0) {
p->Emit(R"objc(
// No extensions in the file and no imports, so no need to generate
// +extensionRegistry.
)objc");
} else {
p->Emit(R"objc(
// No extensions in the file and none of the imports (direct or indirect)
// defined extensions, so no need to generate +extensionRegistry.
)objc");
}
} else {
EmitRootExtensionRegistryImplementation(p, deps_with_extensions);
}
}},
},
R"objc(
#pragma mark - $root_class_name$
@implementation $root_class_name$
if (is_bundled_proto_) {
// This is basically a clone of ImportWriter::PrintRuntimeImports() but
// without the CPP symbol gate, since within the bundled files, that isn't
// needed.
std::string import_prefix = generation_options_.runtime_import_prefix;
if (!import_prefix.empty()) {
import_prefix += "/";
}
for (const auto& header : headers_to_import) {
printer->Print("#import \"$import_prefix$$header$\"\n", "import_prefix",
import_prefix, "header", header);
}
} else {
ImportWriter::EmitRuntimeImports(printer, headers_to_import,
generation_options_.runtime_import_prefix,
/* is_bundled_proto = */ false,
/* default_cpp_symbol = */ true);
$root_extension_registry$
@end
)objc");
}
void FileGenerator::EmitRootExtensionRegistryImplementation(
io::Printer* p,
const std::vector<const FileDescriptor*>& deps_with_extensions) const {
p->Emit(
{
{"register_local_extensions",
[&] {
if (extension_generators_.empty()) {
return;
}
p->Emit(R"objc(
static GPBExtensionDescription descriptions[] = {
$register_local_extensions_variable_blocks$
};
for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {
GPBExtensionDescriptor *extension =
[[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]
usesClassRefs:YES];
[registry addExtension:extension];
[self globallyRegisterExtension:extension];
[extension release];
}
)objc");
}},
{"register_local_extensions_variable_blocks",
[&] {
for (const auto& generator : extension_generators_) {
generator->GenerateStaticVariablesInitialization(p);
}
}},
{"register_imports",
[&] {
if (deps_with_extensions.empty()) {
p->Emit(R"objc(
// None of the imports (direct or indirect) defined extensions, so no need to add
// them to this registry.
)objc");
} else {
p->Emit(R"objc(
// Merge in the imports (direct or indirect) that defined extensions.
)objc");
for (const auto& dep : deps_with_extensions) {
p->Emit({{"dependency", FileClassName(dep)}},
R"objc(
[registry addExtensions:[$dependency$ extensionRegistry]];
)objc");
}
}
}},
},
R"objc(
+ (GPBExtensionRegistry*)extensionRegistry {
// This is called by +initialize so there is no need to worry
// about thread safety and initialization of registry.
static GPBExtensionRegistry* registry = nil;
if (!registry) {
GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
registry = [[GPBExtensionRegistry alloc] init];
$register_local_extensions$;
$register_imports$
}
return registry;
}
)objc");
}
void FileGenerator::EmitFileDescriptorImplementation(io::Printer* p) const {
// File descriptor only needed if there are messages to use it.
if (message_generators_.empty()) {
return;
}
printer->Print("\n");
std::string syntax;
switch (file_->syntax()) {
case FileDescriptor::SYNTAX_UNKNOWN:
syntax = "GPBFileSyntaxUnknown";
break;
case FileDescriptor::SYNTAX_PROTO2:
syntax = "GPBFileSyntaxProto2";
break;
case FileDescriptor::SYNTAX_PROTO3:
syntax = "GPBFileSyntaxProto3";
break;
}
const std::string objc_prefix(FileClassPrefix(file_));
p->Emit(
{
{"root_class_name", root_class_name_},
{"package", file_->package()},
{"objc_prefix", objc_prefix},
{"objc_prefix_arg",
[&] {
if (!objc_prefix.empty()) {
p->Emit(R"objc(
objcPrefix:@"$objc_prefix$"
)objc");
}
}},
{"syntax", syntax},
},
R"objc(
#pragma mark - $root_class_name$_FileDescriptor
static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {
// This is called by +initialize so there is no need to worry
// about thread safety of the singleton.
static GPBFileDescriptor *descriptor = NULL;
if (!descriptor) {
GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"$package$"
$objc_prefix_arg$;
syntax:$syntax$];
}
return descriptor;
}
)objc");
}
} // namespace objectivec

@ -80,8 +80,16 @@ class FileGenerator {
FileGenerator(const FileGenerator&) = delete;
FileGenerator& operator=(const FileGenerator&) = delete;
void GenerateSource(io::Printer* printer) const;
void GenerateHeader(io::Printer* printer) const;
void GenerateHeader(io::Printer* p) const;
void GenerateSource(io::Printer* p) const;
void EmitRootImplementation(
io::Printer* p,
const std::vector<const FileDescriptor*>& deps_with_extensions) const;
void EmitRootExtensionRegistryImplementation(
io::Printer* p,
const std::vector<const FileDescriptor*>& deps_with_extensions) const;
void EmitFileDescriptorImplementation(io::Printer* p) const;
private:
const FileDescriptor* file_;
@ -94,10 +102,6 @@ class FileGenerator {
std::vector<std::unique_ptr<MessageGenerator>> message_generators_;
// The first file_->extension_count() are the extensions at file level scope.
std::vector<std::unique_ptr<ExtensionGenerator>> extension_generators_;
void PrintFileRuntimePreamble(
io::Printer* printer,
const std::vector<std::string>& headers_to_import) const;
};
} // namespace objectivec

@ -196,22 +196,14 @@ void ImportWriter::EmitFileImports(io::Printer* p) const {
void ImportWriter::EmitRuntimeImports(io::Printer* p,
bool default_cpp_symbol) const {
EmitRuntimeImports(p, protobuf_imports_, runtime_import_prefix_,
for_bundled_proto_, default_cpp_symbol);
}
void ImportWriter::EmitRuntimeImports(
io::Printer* p, const std::vector<std::string>& headers_to_import,
const std::string& runtime_import_prefix, bool is_bundled_proto,
bool default_cpp_symbol) {
// Given an override, use that.
if (!runtime_import_prefix.empty()) {
if (!runtime_import_prefix_.empty()) {
p->Emit(
{
{"import_prefix", runtime_import_prefix},
{"import_prefix", runtime_import_prefix_},
{"imports",
[&] {
for (const auto& header : headers_to_import) {
for (const auto& header : protobuf_imports_) {
p->Emit({{"header", header}},
R"objc(
#import "$import_prefix$/$header$"
@ -227,13 +219,13 @@ void ImportWriter::EmitRuntimeImports(
}
// If bundled, no need to do the framework support below.
if (is_bundled_proto) {
GOOGLE_CHECK(!default_cpp_symbol);
if (for_bundled_proto_) {
GOOGLE_DCHECK(!default_cpp_symbol);
p->Emit(
{
{"imports",
[&] {
for (const auto& header : headers_to_import) {
for (const auto& header : protobuf_imports_) {
p->Emit({{"header", header}},
R"objc(
#import "$header$"
@ -269,7 +261,7 @@ void ImportWriter::EmitRuntimeImports(
{"framework_name", ProtobufLibraryFrameworkName},
{"framework_imports",
[&] {
for (const auto& header : headers_to_import) {
for (const auto& header : protobuf_imports_) {
p->Emit({{"header", header}},
R"objc(
#import <$framework_name$/$header$>
@ -278,7 +270,7 @@ void ImportWriter::EmitRuntimeImports(
}},
{"raw_imports",
[&] {
for (const auto& header : headers_to_import) {
for (const auto& header : protobuf_imports_) {
p->Emit({{"header", header}},
R"objc(
#import "$header$"

@ -59,11 +59,6 @@ class ImportWriter {
void EmitFileImports(io::Printer* p) const;
void EmitRuntimeImports(io::Printer* p, bool default_cpp_symbol) const;
static void EmitRuntimeImports(
io::Printer* p, const std::vector<std::string>& headers_to_import,
const std::string& runtime_import_prefix, bool is_bundled_proto = false,
bool default_cpp_symbol = false);
private:
void ParseFrameworkMappings();

Loading…
Cancel
Save