• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 #include "google/protobuf/compiler/objectivec/file.h"
9 
10 #include <algorithm>
11 #include <cstddef>
12 #include <cstdint>
13 #include <functional>
14 #include <iterator>
15 #include <memory>
16 #include <string>
17 #include <vector>
18 
19 #include "absl/container/btree_set.h"
20 #include "absl/container/flat_hash_map.h"
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/log/absl_check.h"
23 #include "absl/strings/str_cat.h"
24 #include "absl/strings/str_join.h"
25 #include "absl/strings/string_view.h"
26 #include "google/protobuf/compiler/code_generator.h"
27 #include "google/protobuf/compiler/objectivec/enum.h"
28 #include "google/protobuf/compiler/objectivec/extension.h"
29 #include "google/protobuf/compiler/objectivec/helpers.h"
30 #include "google/protobuf/compiler/objectivec/import_writer.h"
31 #include "google/protobuf/compiler/objectivec/message.h"
32 #include "google/protobuf/compiler/objectivec/names.h"
33 #include "google/protobuf/compiler/objectivec/options.h"
34 #include "google/protobuf/descriptor.h"
35 #include "google/protobuf/descriptor.pb.h"
36 #include "google/protobuf/io/printer.h"
37 
38 namespace google {
39 namespace protobuf {
40 namespace compiler {
41 namespace objectivec {
42 
43 namespace {
44 
45 // This is also found in GPBBootstrap.h, and needs to be kept in sync.
46 const int32_t GOOGLE_PROTOBUF_OBJC_VERSION = 30007;
47 
48 const char* kHeaderExtension = ".pbobjc.h";
49 
IsMapEntryMessage(const Descriptor * descriptor)50 bool IsMapEntryMessage(const Descriptor* descriptor) {
51   return descriptor->options().map_entry();
52 }
53 
54 // Checks if a message contains extension definitions (on the message or
55 // a nested message under it). `include_custom_options` decides if custom
56 // options count as extensions.
MessageContainsExtensions(const Descriptor * message,bool include_custom_options)57 bool MessageContainsExtensions(const Descriptor* message,
58                                bool include_custom_options) {
59   if (message->extension_count() > 0) {
60     if (include_custom_options) {
61       return true;
62     }
63     for (int i = 0; i < message->extension_count(); i++) {
64       if (!ExtensionIsCustomOption(message->extension(i))) {
65         return true;
66       }
67     }
68   }
69   for (int i = 0; i < message->nested_type_count(); i++) {
70     if (MessageContainsExtensions(message->nested_type(i),
71                                   include_custom_options)) {
72       return true;
73     }
74   }
75   return false;
76 }
77 
78 // Checks if the file contains extensions definitions (at the root or
79 // nested under a message). `include_custom_options` decides if custom
80 // options count as extensions.
FileContainsExtensions(const FileDescriptor * file,bool include_custom_options)81 bool FileContainsExtensions(const FileDescriptor* file,
82                             bool include_custom_options) {
83   if (file->extension_count() > 0) {
84     if (include_custom_options) {
85       return true;
86     }
87     for (int i = 0; i < file->extension_count(); i++) {
88       if (!ExtensionIsCustomOption(file->extension(i))) {
89         return true;
90       }
91     }
92   }
93   for (int i = 0; i < file->message_type_count(); i++) {
94     if (MessageContainsExtensions(file->message_type(i),
95                                   include_custom_options)) {
96       return true;
97     }
98   }
99   return false;
100 }
101 
IsDirectDependency(const FileDescriptor * dep,const FileDescriptor * file)102 bool IsDirectDependency(const FileDescriptor* dep, const FileDescriptor* file) {
103   for (int i = 0; i < file->dependency_count(); i++) {
104     if (dep == file->dependency(i)) {
105       return true;
106     }
107   }
108   return false;
109 }
110 
111 struct FileDescriptorsOrderedByName {
operator ()google::protobuf::compiler::objectivec::__anonab4920520111::FileDescriptorsOrderedByName112   inline bool operator()(const FileDescriptor* a,
113                          const FileDescriptor* b) const {
114     return a->name() < b->name();
115   }
116 };
117 
MakeDescriptors(const Descriptor * descriptor,const std::string & file_description_name,std::vector<std::unique_ptr<EnumGenerator>> * enum_generators,std::vector<std::unique_ptr<ExtensionGenerator>> * extension_generators,std::vector<std::unique_ptr<MessageGenerator>> * message_generators,const GenerationOptions & generation_options)118 void MakeDescriptors(
119     const Descriptor* descriptor, const std::string& file_description_name,
120     std::vector<std::unique_ptr<EnumGenerator>>* enum_generators,
121     std::vector<std::unique_ptr<ExtensionGenerator>>* extension_generators,
122     std::vector<std::unique_ptr<MessageGenerator>>* message_generators,
123     const GenerationOptions& generation_options) {
124   for (int i = 0; i < descriptor->enum_type_count(); i++) {
125     enum_generators->emplace_back(std::make_unique<EnumGenerator>(
126         descriptor->enum_type(i), generation_options));
127   }
128   for (int i = 0; i < descriptor->nested_type_count(); i++) {
129     const Descriptor* message_type = descriptor->nested_type(i);
130     if (IsMapEntryMessage(message_type)) {
131       // Map entries can't have extensions, or sub messages, they are an
132       // implementation detail of how map<> works.
133       continue;
134     }
135     message_generators->emplace_back(std::make_unique<MessageGenerator>(
136         file_description_name, message_type, generation_options));
137     message_generators->back()->AddExtensionGenerators(extension_generators);
138     MakeDescriptors(message_type, file_description_name, enum_generators,
139                     extension_generators, message_generators,
140                     generation_options);
141   }
142 }
143 
EmitLinkWKTs(absl::string_view name,io::Printer * p)144 void EmitLinkWKTs(absl::string_view name, io::Printer* p) {
145   absl::string_view::size_type last_slash = name.rfind('/');
146   std::string basename;
147   if (last_slash == absl::string_view::npos) {
148     basename = std::string(name);
149   } else {
150     basename = std::string(name.substr(last_slash + 1));
151   }
152 
153   p->Emit({{"basename", StripProto(basename)}},
154           R"objc(
155             // This is to help make sure that the GPBWellKnownTypes.* categories get linked and
156             // developers do not have to use the `-ObjC` linker flag. More information
157             // here: https://medium.com/ios-os-x-development/categories-in-static-libraries-78e41f8ddb96
158             __attribute__((used)) static NSString* $basename$_importCategories(void) {
159               return GPBWellKnownTypesErrorDomain;
160             }
161           )objc");
162   p->Emit("\n");
163 }
164 
EmitSourceFwdDecls(const absl::btree_set<std::string> & fwd_decls,io::Printer * p)165 void EmitSourceFwdDecls(const absl::btree_set<std::string>& fwd_decls,
166                         io::Printer* p) {
167   if (fwd_decls.empty()) {
168     return;
169   }
170 
171   p->Emit({{"fwd_decls", absl::StrJoin(fwd_decls, "\n")}},
172           R"objc(
173             #pragma mark - Objective-C Class declarations
174             // Forward declarations of Objective-C classes that we can use as
175             // static values in struct initializers.
176             // We don't use [Foo class] because it is not a static value.
177             $fwd_decls$
178           )objc");
179   p->Emit("\n");
180 }
181 
182 }  // namespace
183 
184 const FileGenerator::CommonState::MinDepsEntry&
CollectMinimalFileDepsContainingExtensionsInternal(const FileDescriptor * file)185 FileGenerator::CommonState::CollectMinimalFileDepsContainingExtensionsInternal(
186     const FileDescriptor* file) {
187   auto it = deps_info_cache.find(file);
188   if (it != deps_info_cache.end()) {
189     return it->second;
190   }
191 
192   absl::flat_hash_set<const FileDescriptor*> min_deps_collector;
193   absl::flat_hash_set<const FileDescriptor*> transitive_deps_collector;
194   absl::flat_hash_set<const FileDescriptor*> to_prune;
195   for (int i = 0; i < file->dependency_count(); i++) {
196     const FileDescriptor* dep = file->dependency(i);
197     MinDepsEntry dep_info =
198         CollectMinimalFileDepsContainingExtensionsInternal(dep);
199 
200     // Everything the dep covered, this file will also cover.
201     transitive_deps_collector.insert(dep_info.transitive_deps.begin(),
202                                      dep_info.transitive_deps.end());
203     // Prune everything from the dep's covered list in case another dep lists it
204     // as a min dep.
205     to_prune.insert(dep_info.transitive_deps.begin(),
206                     dep_info.transitive_deps.end());
207 
208     // Does the dep have any extensions...
209     if (dep_info.has_extensions) {
210       // Yes -> Add this file, prune its min_deps and add them to the covered
211       // deps.
212       min_deps_collector.insert(dep);
213       to_prune.insert(dep_info.min_deps.begin(), dep_info.min_deps.end());
214       transitive_deps_collector.insert(dep_info.min_deps.begin(),
215                                        dep_info.min_deps.end());
216     } else {
217       // No -> Just use its min_deps.
218       min_deps_collector.insert(dep_info.min_deps.begin(),
219                                 dep_info.min_deps.end());
220     }
221   }
222 
223   const bool file_has_exts =
224       FileContainsExtensions(file, include_custom_options);
225 
226   // Fast path: if nothing to prune or there was only one dep, the prune work is
227   // a waste, skip it.
228   if (to_prune.empty() || file->dependency_count() == 1) {
229     return deps_info_cache
230         .insert(
231             {file,
232              {file_has_exts, min_deps_collector, transitive_deps_collector}})
233         .first->second;
234   }
235 
236   absl::flat_hash_set<const FileDescriptor*> min_deps;
237   std::copy_if(min_deps_collector.begin(), min_deps_collector.end(),
238                std::inserter(min_deps, min_deps.begin()),
239                [&](const FileDescriptor* value) {
240                  return to_prune.find(value) == to_prune.end();
241                });
242   return deps_info_cache
243       .insert({file, {file_has_exts, min_deps, transitive_deps_collector}})
244       .first->second;
245 }
246 
247 // Collect the deps of the given file that contain extensions. This can be used
248 // to create the chain of roots that need to be wired together.
249 //
250 // NOTE: If any changes are made to this and the supporting functions, you will
251 // need to manually validate what the generated code is for the test files:
252 //   objectivec/Tests/unittest_extension_chain_*.proto
253 // There are comments about what the expected code should be line and limited
254 // testing objectivec/Tests/GPBUnittestProtos2.m around compilation (#imports
255 // specifically).
256 std::vector<const FileDescriptor*>
CollectMinimalFileDepsContainingExtensions(const FileDescriptor * file)257 FileGenerator::CommonState::CollectMinimalFileDepsContainingExtensions(
258     const FileDescriptor* file) {
259   absl::flat_hash_set<const FileDescriptor*> min_deps =
260       CollectMinimalFileDepsContainingExtensionsInternal(file).min_deps;
261   // Sort the list since pointer order isn't stable across runs.
262   std::vector<const FileDescriptor*> result(min_deps.begin(), min_deps.end());
263   std::sort(result.begin(), result.end(), FileDescriptorsOrderedByName());
264   return result;
265 }
266 
FileGenerator(Edition edition,const FileDescriptor * file,const GenerationOptions & generation_options,CommonState & common_state)267 FileGenerator::FileGenerator(Edition edition, const FileDescriptor* file,
268                              const GenerationOptions& generation_options,
269                              CommonState& common_state)
270     : edition_(edition),
271       file_(file),
272       generation_options_(generation_options),
273       common_state_(&common_state),
274       root_class_name_(FileClassName(file)),
275       file_description_name_(FileClassName(file) + "_FileDescription"),
276       is_bundled_proto_(IsProtobufLibraryBundledProtoFile(file)) {
277   for (int i = 0; i < file_->enum_type_count(); i++) {
278     enum_generators_.emplace_back(std::make_unique<EnumGenerator>(
279         file_->enum_type(i), generation_options));
280   }
281   for (int i = 0; i < file_->extension_count(); i++) {
282     const FieldDescriptor* extension = file_->extension(i);
283     if (!generation_options.strip_custom_options ||
284         !ExtensionIsCustomOption(extension)) {
285       extension_generators_.push_back(std::make_unique<ExtensionGenerator>(
286           root_class_name_, extension, generation_options));
287     }
288   }
289   file_scoped_extension_count_ = extension_generators_.size();
290   for (int i = 0; i < file_->message_type_count(); i++) {
291     const Descriptor* message_type = file_->message_type(i);
292     if (IsMapEntryMessage(message_type)) {
293       // Map entries can't have extensions, or sub messages, they are an
294       // implementation detail of how map<> works.
295       continue;
296     }
297     message_generators_.emplace_back(std::make_unique<MessageGenerator>(
298         file_description_name_, message_type, generation_options));
299     message_generators_.back()->AddExtensionGenerators(&extension_generators_);
300     MakeDescriptors(message_type, file_description_name_, &enum_generators_,
301                     &extension_generators_, &message_generators_,
302                     generation_options);
303   }
304 }
305 
GenerateHeader(io::Printer * p) const306 void FileGenerator::GenerateHeader(io::Printer* p) const {
307   GenerateFile(p, GeneratedFileType::kHeader, [&] {
308     absl::btree_set<std::string> fwd_decls;
309     for (const auto& generator : message_generators_) {
310       generator->DetermineForwardDeclarations(&fwd_decls,
311                                               /* include_external_types = */
312                                               HeadersUseForwardDeclarations());
313     }
314 
315     p->Emit("CF_EXTERN_C_BEGIN\n\n");
316 
317     if (!fwd_decls.empty()) {
318       p->Emit({{"fwd_decls", absl::StrJoin(fwd_decls, "\n")}},
319               "$fwd_decls$\n\n");
320     }
321 
322     p->Emit("NS_ASSUME_NONNULL_BEGIN\n\n");
323 
324     for (const auto& generator : enum_generators_) {
325       generator->GenerateHeader(p);
326     }
327 
328     // For extensions to chain together, the Root gets created even if there
329     // are no extensions.
330     p->Emit(R"objc(
331       #pragma mark - $root_class_name$
332 
333       /**
334        * Exposes the extension registry for this file.
335        *
336        * The base class provides:
337        * @code
338        *   + (GPBExtensionRegistry *)extensionRegistry;
339        * @endcode
340        * which is a @c GPBExtensionRegistry that includes all the extensions defined by
341        * this file and all files that it depends on.
342        **/
343       GPB_FINAL @interface $root_class_name$ : GPBRootObject
344       @end
345     )objc");
346     p->Emit("\n");
347 
348     // The dynamic methods block is only needed if there are extensions that are
349     // file level scoped (not message scoped). The first
350     // file_scoped_extension_count_ of extension_generators_ are the file scoped
351     // ones.
352     if (file_scoped_extension_count_) {
353       p->Emit("@interface $root_class_name$ (DynamicMethods)\n");
354 
355       for (size_t i = 0; i < file_scoped_extension_count_; i++) {
356         extension_generators_[i]->GenerateMembersHeader(p);
357       }
358 
359       p->Emit("@end\n\n");
360     }
361 
362     for (const auto& generator : message_generators_) {
363       generator->GenerateMessageHeader(p);
364     }
365 
366     p->Emit(R"objc(
367       NS_ASSUME_NONNULL_END
368 
369       CF_EXTERN_C_END
370     )objc");
371   });
372 }
373 
GenerateSource(io::Printer * p) const374 void FileGenerator::GenerateSource(io::Printer* p) const {
375   std::vector<const FileDescriptor*> deps_with_extensions =
376       common_state_->CollectMinimalFileDepsContainingExtensions(file_);
377   GeneratedFileOptions file_options;
378   file_options.forced_files_to_import = deps_with_extensions;
379 
380   absl::btree_set<std::string> fwd_decls;
381   for (const auto& generator : message_generators_) {
382     generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
383   }
384   for (const auto& generator : extension_generators_) {
385     generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
386   }
387 
388   // The generated code for oneof's uses direct ivar access, suppress the
389   // warning in case developer turn that on in the context they compile the
390   // generated code.
391   for (const auto& generator : message_generators_) {
392     if (generator->IncludesOneOfDefinition()) {
393       file_options.ignored_warnings.push_back("direct-ivar-access");
394       break;
395     }
396   }
397   if (!fwd_decls.empty()) {
398     file_options.ignored_warnings.push_back("dollar-in-identifier-extension");
399   }
400 
401   // Enum implementation uses atomic in the generated code, so add
402   // the system import as needed.
403   if (!enum_generators_.empty()) {
404     file_options.extra_system_headers.push_back("stdatomic.h");
405   }
406 
407   GenerateFile(p, GeneratedFileType::kSource, file_options, [&] {
408     EmitSourceFwdDecls(fwd_decls, p);
409     EmitRootImplementation(p, deps_with_extensions);
410     EmitFileDescription(p);
411 
412     if (is_bundled_proto_ && HasWKTWithObjCCategory(file_)) {
413       EmitLinkWKTs(file_->name(), p);
414     }
415 
416     for (const auto& generator : enum_generators_) {
417       generator->GenerateSource(p);
418     }
419     for (const auto& generator : message_generators_) {
420       generator->GenerateSource(p);
421     }
422   });
423 }
424 
GenerateGlobalSource(io::Printer * p) const425 void FileGenerator::GenerateGlobalSource(io::Printer* p) const {
426   ABSL_CHECK(!is_bundled_proto_)
427       << "Bundled protos aren't expected to use multi source generation.";
428   std::vector<const FileDescriptor*> deps_with_extensions =
429       common_state_->CollectMinimalFileDepsContainingExtensions(file_);
430   GeneratedFileOptions file_options;
431   file_options.forced_files_to_import = deps_with_extensions;
432 
433   absl::btree_set<std::string> fwd_decls;
434   for (const auto& generator : extension_generators_) {
435     generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
436   }
437 
438   if (!fwd_decls.empty()) {
439     file_options.ignored_warnings.push_back("dollar-in-identifier-extension");
440   }
441 
442   GenerateFile(p, GeneratedFileType::kSource, file_options, [&] {
443     EmitSourceFwdDecls(fwd_decls, p);
444     EmitRootImplementation(p, deps_with_extensions);
445   });
446 }
447 
GenerateSourceForEnums(io::Printer * p) const448 void FileGenerator::GenerateSourceForEnums(io::Printer* p) const {
449   ABSL_CHECK(!is_bundled_proto_)
450       << "Bundled protos aren't expected to use multi source generation.";
451   // Enum implementation uses atomic in the generated code.
452   GeneratedFileOptions file_options;
453   file_options.extra_system_headers.push_back("stdatomic.h");
454 
455   GenerateFile(p, GeneratedFileType::kSource, file_options, [&] {
456     for (const auto& generator : enum_generators_) {
457       generator->GenerateSource(p);
458     }
459   });
460 }
461 
GenerateSourceForMessage(int idx,io::Printer * p) const462 void FileGenerator::GenerateSourceForMessage(int idx, io::Printer* p) const {
463   ABSL_CHECK(!is_bundled_proto_)
464       << "Bundled protos aren't expected to use multi source generation.";
465   const auto& generator = message_generators_[idx];
466 
467   absl::btree_set<std::string> fwd_decls;
468   generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
469 
470   GeneratedFileOptions file_options;
471   // The generated code for oneof's uses direct ivar access, suppress the
472   // warning in case developer turn that on in the context they compile the
473   // generated code.
474   if (generator->IncludesOneOfDefinition()) {
475     file_options.ignored_warnings.push_back("direct-ivar-access");
476   }
477 
478   GenerateFile(p, GeneratedFileType::kSource, file_options, [&] {
479     EmitSourceFwdDecls(fwd_decls, p);
480     EmitFileDescription(p);
481     generator->GenerateSource(p);
482   });
483 }
484 
GenerateFile(io::Printer * p,GeneratedFileType file_type,const GeneratedFileOptions & file_options,std::function<void ()> body) const485 void FileGenerator::GenerateFile(io::Printer* p, GeneratedFileType file_type,
486                                  const GeneratedFileOptions& file_options,
487                                  std::function<void()> body) const {
488   ImportWriter import_writer(
489       generation_options_.generate_for_named_framework,
490       generation_options_.named_framework_to_proto_path_mappings_path,
491       generation_options_.runtime_import_prefix,
492       /* for_bundled_proto = */ is_bundled_proto_);
493   const std::string header_extension(kHeaderExtension);
494 
495   absl::flat_hash_set<const FileDescriptor*> file_imports;
496   switch (file_type) {
497     case GeneratedFileType::kHeader:
498       // Generated files bundled with the library get minimal imports,
499       // everything else gets the wrapper so everything is usable.
500       if (is_bundled_proto_) {
501         import_writer.AddRuntimeImport("GPBDescriptor.h");
502         import_writer.AddRuntimeImport("GPBMessage.h");
503         import_writer.AddRuntimeImport("GPBRootObject.h");
504       } else {
505         import_writer.AddRuntimeImport("GPBProtocolBuffers.h");
506       }
507       if (HeadersUseForwardDeclarations()) {
508         // #import any headers for "public imports" in the proto file.
509         for (int i = 0; i < file_->public_dependency_count(); i++) {
510           file_imports.insert(file_->public_dependency(i));
511         }
512       } else if (generation_options_.generate_minimal_imports) {
513         DetermineNeededDeps(&file_imports, PublicDepsHandling::kForceInclude);
514       } else {
515         for (int i = 0; i < file_->dependency_count(); i++) {
516           file_imports.insert(file_->dependency(i));
517         }
518       }
519       break;
520     case GeneratedFileType::kSource:
521       import_writer.AddRuntimeImport("GPBProtocolBuffers_RuntimeSupport.h");
522       if (is_bundled_proto_ && HasWKTWithObjCCategory(file_)) {
523         import_writer.AddRuntimeImport("GPBWellKnownTypes.h");
524       }
525       import_writer.AddFile(file_, header_extension);
526       if (HeadersUseForwardDeclarations()) {
527         if (generation_options_.generate_minimal_imports) {
528           DetermineNeededDeps(&file_imports, PublicDepsHandling::kExclude);
529         } else {
530           // #import the headers for anything that a plain dependency of this
531           // proto file (that means they were just an include, not a "public"
532           // include).
533           absl::flat_hash_set<absl::string_view> public_import_names;
534           for (int i = 0; i < file_->public_dependency_count(); i++) {
535             public_import_names.insert(file_->public_dependency(i)->name());
536           }
537           for (int i = 0; i < file_->dependency_count(); i++) {
538             const FileDescriptor* dep = file_->dependency(i);
539             if (!public_import_names.contains(dep->name())) {
540               file_imports.insert(dep);
541             }
542           }
543         }
544       }
545       break;
546   }
547 
548   // If a forced file was a direct dep, move it into the file_imports.
549   std::vector<const FileDescriptor*> extra_files_to_import;
550   for (const auto& dep : file_options.forced_files_to_import) {
551     if (IsDirectDependency(dep, file_)) {
552       file_imports.insert(dep);
553     } else {
554       extra_files_to_import.push_back(dep);
555     }
556   }
557 
558   if (!file_imports.empty()) {
559     // Output the file_imports in the order they were listed as dependencies.
560     for (int i = 0; i < file_->dependency_count(); i++) {
561       const FileDescriptor* dep = file_->dependency(i);
562       if (file_imports.contains(dep)) {
563         import_writer.AddFile(file_->dependency(i), header_extension);
564         file_imports.erase(dep);
565       }
566     }
567     if (!file_imports.empty()) {
568       // If there are still things in file_imports, then there were files that
569       // were public imports into the non public imports, add those files are
570       // needed to define the types also.
571       //
572       // Sort them (to get stable generation), and add them to the extra files
573       // to imports.
574 
575       // This can really only happen in minimal imports mode, every other case,
576       // it shouldn't happen.
577       ABSL_CHECK(generation_options_.generate_minimal_imports);
578       std::vector<const FileDescriptor*> still_needed(file_imports.begin(),
579                                                       file_imports.end());
580       std::sort(still_needed.begin(), still_needed.end(),
581                 FileDescriptorsOrderedByName());
582       extra_files_to_import.insert(extra_files_to_import.end(),
583                                    still_needed.begin(), still_needed.end());
584     }
585   }
586 
587   for (const auto& dep : extra_files_to_import) {
588     import_writer.AddFile(dep, header_extension);
589   }
590 
591   // Some things for all Emit() calls to have access too.
592   auto vars = p->WithVars({
593       {// Avoid the directive within the template strings as the tool would
594        // then honor the directives within the generators sources.
595        "clangfmt", "clang-format"},
596       {"root_class_name", root_class_name_},
597   });
598 
599   p->Emit(
600       {
601           {"no_checked_in",
602            "NO CHECKED-IN"
603            // Intentional line breaker
604            " PROTOBUF GENCODE"},
605           {"filename", file_->name()},
606           {"google_protobuf_objc_version", GOOGLE_PROTOBUF_OBJC_VERSION},
607           {"runtime_imports",
608            [&] {
609              import_writer.PrintRuntimeImports(
610                  p, /* default_cpp_symbol = */ !is_bundled_proto_);
611            }},
612           {"extra_system_imports",
613            [&] {
614              if (file_options.extra_system_headers.empty()) {
615                return;
616              }
617              for (const auto& system_header :
618                   file_options.extra_system_headers) {
619                p->Emit({{"header", system_header}},
620                        R"objc(
621                          #import <$header$>
622                        )objc");
623              }
624              p->Emit("\n");
625            }},
626           {"file_imports", [&] { import_writer.PrintFileImports(p); }},
627           {"extra_warnings",
628            [&] {
629              for (const auto& warning : file_options.ignored_warnings) {
630                p->Emit({{"warning", warning}},
631                        R"objc(
632                          #pragma clang diagnostic ignored "-W$warning$"
633                        )objc");
634              }
635            }},
636       },
637       R"objc(
638         // Generated by the protocol buffer compiler.  DO NOT EDIT!
639         // $no_checked_in$
640         // $clangfmt$ off
641         // source: $filename$
642 
643         $runtime_imports$
644 
645         #if GOOGLE_PROTOBUF_OBJC_VERSION < $google_protobuf_objc_version$
646         #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
647         #endif
648         #if $google_protobuf_objc_version$ < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
649         #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
650         #endif
651 
652         $extra_system_imports$
653         $file_imports$
654         // @@protoc_insertion_point(imports)
655 
656         #pragma clang diagnostic push
657         #pragma clang diagnostic ignored "-Wdeprecated-declarations"
658         $extra_warnings$
659       )objc");
660 
661   p->Emit("\n");
662 
663   body();
664 
665   p->Emit("\n");
666 
667   p->Emit(R"objc(
668     #pragma clang diagnostic pop
669 
670     // @@protoc_insertion_point(global_scope)
671 
672     // $clangfmt$ on
673   )objc");
674 }
675 
EmitRootImplementation(io::Printer * p,const std::vector<const FileDescriptor * > & deps_with_extensions) const676 void FileGenerator::EmitRootImplementation(
677     io::Printer* p,
678     const std::vector<const FileDescriptor*>& deps_with_extensions) const {
679   p->Emit(
680       R"objc(
681         #pragma mark - $root_class_name$
682 
683         @implementation $root_class_name$
684       )objc");
685 
686   p->Emit("\n");
687 
688   // If there were any extensions or this file has any dependencies,
689   // output a registry to override to create the file specific
690   // registry.
691   if (extension_generators_.empty() && deps_with_extensions.empty()) {
692     p->Emit(R"objc(
693       // No extensions in the file and no imports or none of the imports (direct or
694       // indirect) defined extensions, so no need to generate +extensionRegistry.
695     )objc");
696   } else {
697     EmitRootExtensionRegistryImplementation(p, deps_with_extensions);
698   }
699 
700   p->Emit("\n");
701   p->Emit("@end\n\n");
702 }
703 
EmitRootExtensionRegistryImplementation(io::Printer * p,const std::vector<const FileDescriptor * > & deps_with_extensions) const704 void FileGenerator::EmitRootExtensionRegistryImplementation(
705     io::Printer* p,
706     const std::vector<const FileDescriptor*>& deps_with_extensions) const {
707   p->Emit(
708       {
709           {"register_local_extensions",
710            [&] {
711              if (extension_generators_.empty()) {
712                return;
713              }
714              p->Emit(
715                  {
716                      {"register_local_extensions_variable_blocks",
717                       [&] {
718                         for (const auto& generator : extension_generators_) {
719                           generator->GenerateStaticVariablesInitialization(p);
720                         }
721                       }},
722                  },
723                  R"objc(
724                    static GPBExtensionDescription descriptions[] = {
725                      $register_local_extensions_variable_blocks$
726                    };
727                    for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {
728                      GPBExtensionDescriptor *extension =
729                          [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]
730                                                                         usesClassRefs:YES];
731                      [registry addExtension:extension];
732                      [self globallyRegisterExtension:extension];
733                      [extension release];
734                    }
735                  )objc");
736            }},
737           {"register_imports",
738            [&] {
739              if (deps_with_extensions.empty()) {
740                p->Emit(R"objc(
741                  // None of the imports (direct or indirect) defined extensions, so no need to add
742                  // them to this registry.
743                )objc");
744              } else {
745                p->Emit(R"objc(
746                  // Merge in the imports (direct or indirect) that defined extensions.
747                )objc");
748                for (const auto& dep : deps_with_extensions) {
749                  p->Emit({{"dependency", FileClassName(dep)}},
750                          R"objc(
751                            [registry addExtensions:[$dependency$ extensionRegistry]];
752                          )objc");
753                }
754              }
755            }},
756       },
757       R"objc(
758         + (GPBExtensionRegistry*)extensionRegistry {
759           // This is called by +initialize so there is no need to worry
760           // about thread safety and initialization of registry.
761           static GPBExtensionRegistry* registry = nil;
762           if (!registry) {
763             GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
764             registry = [[GPBExtensionRegistry alloc] init];
765             $register_local_extensions$;
766             $register_imports$
767           }
768           return registry;
769         }
770       )objc");
771 }
772 
EmitFileDescription(io::Printer * p) const773 void FileGenerator::EmitFileDescription(io::Printer* p) const {
774   // File descriptor only needed if there are messages to use it.
775   if (message_generators_.empty()) {
776     return;
777   }
778 
779   const std::string objc_prefix(FileClassPrefix(file_));
780   std::string syntax;
781   if (generation_options_.experimental_strip_nonfunctional_codegen) {
782     // Doesn't matter for current sources, use Unknown as a marker for this
783     // mode.
784     syntax = "GPBFileSyntaxUnknown";
785   } else {
786     switch (edition_) {
787       case Edition::EDITION_UNKNOWN:
788         syntax = "GPBFileSyntaxUnknown";
789         break;
790       case Edition::EDITION_PROTO2:
791         syntax = "GPBFileSyntaxProto2";
792         break;
793       case Edition::EDITION_PROTO3:
794         syntax = "GPBFileSyntaxProto3";
795         break;
796       default:
797         syntax = "GPBFileSyntaxProtoEditions";
798         break;
799     }
800   }
801 
802   p->Emit({{"file_description_name", file_description_name_},
803            {"package_value", file_->package().empty()
804                                  ? "NULL"
805                                  : absl::StrCat("\"", file_->package(), "\"")},
806            {"prefix_value",
807             objc_prefix.empty() && !file_->options().has_objc_class_prefix()
808                 ? "NULL"
809                 : absl::StrCat("\"", objc_prefix, "\"")},
810            {"syntax", syntax}},
811           R"objc(
812             static GPBFileDescription $file_description_name$ = {
813               .package = $package_value$,
814               .prefix = $prefix_value$,
815               .syntax = $syntax$
816             };
817           )objc");
818   p->Emit("\n");
819 }
820 
DetermineNeededDeps(absl::flat_hash_set<const FileDescriptor * > * deps,PublicDepsHandling public_deps_handling) const821 void FileGenerator::DetermineNeededDeps(
822     absl::flat_hash_set<const FileDescriptor*>* deps,
823     PublicDepsHandling public_deps_handling) const {
824   // This logic captures the deps that are needed for types thus removing the
825   // ones that are only deps because they provide the definitions for custom
826   // options. If protoc gets something like "import options" then this logic can
827   // go away as the non "import options" deps would be the ones needed.
828 
829   if (public_deps_handling == PublicDepsHandling::kForceInclude) {
830     for (int i = 0; i < file_->public_dependency_count(); i++) {
831       deps->insert(file_->public_dependency(i));
832     }
833   }
834 
835   for (const auto& generator : message_generators_) {
836     generator->DetermineNeededFiles(deps);
837   }
838   for (const auto& generator : extension_generators_) {
839     generator->DetermineNeededFiles(deps);
840   }
841 
842   if (public_deps_handling == PublicDepsHandling::kExclude) {
843     for (int i = 0; i < file_->public_dependency_count(); i++) {
844       deps->erase(file_);
845     }
846   }
847 }
848 
849 }  // namespace objectivec
850 }  // namespace compiler
851 }  // namespace protobuf
852 }  // namespace google
853