diff --git a/src/google/protobuf/compiler/objectivec/file.cc b/src/google/protobuf/compiler/objectivec/file.cc index 15ec93dece..ed7b13cb8f 100644 --- a/src/google/protobuf/compiler/objectivec/file.cc +++ b/src/google/protobuf/compiler/objectivec/file.cc @@ -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 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 \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 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 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 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::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::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 \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::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 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& 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& 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& 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 diff --git a/src/google/protobuf/compiler/objectivec/file.h b/src/google/protobuf/compiler/objectivec/file.h index 6ef834bbc4..c60f0672f7 100644 --- a/src/google/protobuf/compiler/objectivec/file.h +++ b/src/google/protobuf/compiler/objectivec/file.h @@ -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& deps_with_extensions) const; + void EmitRootExtensionRegistryImplementation( + io::Printer* p, + const std::vector& deps_with_extensions) const; + void EmitFileDescriptorImplementation(io::Printer* p) const; private: const FileDescriptor* file_; @@ -94,10 +102,6 @@ class FileGenerator { std::vector> message_generators_; // The first file_->extension_count() are the extensions at file level scope. std::vector> extension_generators_; - - void PrintFileRuntimePreamble( - io::Printer* printer, - const std::vector& headers_to_import) const; }; } // namespace objectivec diff --git a/src/google/protobuf/compiler/objectivec/import_writer.cc b/src/google/protobuf/compiler/objectivec/import_writer.cc index 1450166050..10af534c39 100644 --- a/src/google/protobuf/compiler/objectivec/import_writer.cc +++ b/src/google/protobuf/compiler/objectivec/import_writer.cc @@ -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& 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$" diff --git a/src/google/protobuf/compiler/objectivec/import_writer.h b/src/google/protobuf/compiler/objectivec/import_writer.h index 2fec890d9a..fe75b5fd3c 100644 --- a/src/google/protobuf/compiler/objectivec/import_writer.h +++ b/src/google/protobuf/compiler/objectivec/import_writer.h @@ -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& headers_to_import, - const std::string& runtime_import_prefix, bool is_bundled_proto = false, - bool default_cpp_symbol = false); - private: void ParseFrameworkMappings();