1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include <google/protobuf/compiler/objectivec/objectivec_file.h>
32 #include <google/protobuf/compiler/objectivec/objectivec_enum.h>
33 #include <google/protobuf/compiler/objectivec/objectivec_extension.h>
34 #include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
35 #include <google/protobuf/compiler/objectivec/objectivec_message.h>
36 #include <google/protobuf/compiler/code_generator.h>
37 #include <google/protobuf/io/printer.h>
38 #include <google/protobuf/io/zero_copy_stream_impl.h>
39 #include <google/protobuf/stubs/stl_util.h>
40 #include <google/protobuf/stubs/strutil.h>
41 #include <algorithm> // std::find()
42 #include <iostream>
43 #include <sstream>
44 
45 // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
46 // error cases, so it seems to be ok to use as a back door for errors.
47 
48 namespace google {
49 namespace protobuf {
50 namespace compiler {
51 namespace objectivec {
52 
53 namespace {
54 
55 // This is also found in GPBBootstrap.h, and needs to be kept in sync.
56 const int32_t GOOGLE_PROTOBUF_OBJC_VERSION = 30004;
57 
58 const char* kHeaderExtension = ".pbobjc.h";
59 
BundledFileName(const FileDescriptor * file)60 std::string BundledFileName(const FileDescriptor* file) {
61   return "GPB" + FilePathBasename(file) + kHeaderExtension;
62 }
63 
64 // Checks if a message contains any enums definitions (on the message or
65 // a nested message under it).
MessageContainsEnums(const Descriptor * message)66 bool MessageContainsEnums(const Descriptor* message) {
67   if (message->enum_type_count() > 0) {
68     return true;
69   }
70   for (int i = 0; i < message->nested_type_count(); i++) {
71     if (MessageContainsEnums(message->nested_type(i))) {
72       return true;
73     }
74   }
75   return false;
76 }
77 
78 // Checks if a message contains any extension definitions (on the message or
79 // a nested message under it).
MessageContainsExtensions(const Descriptor * message)80 bool MessageContainsExtensions(const Descriptor* message) {
81   if (message->extension_count() > 0) {
82     return true;
83   }
84   for (int i = 0; i < message->nested_type_count(); i++) {
85     if (MessageContainsExtensions(message->nested_type(i))) {
86       return true;
87     }
88   }
89   return false;
90 }
91 
92 // Checks if the file contains any enum definitions (at the root or
93 // nested under a message).
FileContainsEnums(const FileDescriptor * file)94 bool FileContainsEnums(const FileDescriptor* file) {
95   if (file->enum_type_count() > 0) {
96     return true;
97   }
98   for (int i = 0; i < file->message_type_count(); i++) {
99     if (MessageContainsEnums(file->message_type(i))) {
100       return true;
101     }
102   }
103   return false;
104 }
105 
106 // Checks if the file contains any extensions definitions (at the root or
107 // nested under a message).
FileContainsExtensions(const FileDescriptor * file)108 bool FileContainsExtensions(const FileDescriptor* file) {
109   if (file->extension_count() > 0) {
110     return true;
111   }
112   for (int i = 0; i < file->message_type_count(); i++) {
113     if (MessageContainsExtensions(file->message_type(i))) {
114       return true;
115     }
116   }
117   return false;
118 }
119 
IsDirectDependency(const FileDescriptor * dep,const FileDescriptor * file)120 bool IsDirectDependency(const FileDescriptor* dep, const FileDescriptor* file) {
121   for (int i = 0; i < file->dependency_count(); i++) {
122     if (dep == file->dependency(i)) {
123       return true;
124     }
125   }
126   return false;
127 }
128 
129 struct FileDescriptorsOrderedByName {
operator ()google::protobuf::compiler::objectivec::__anon1ffcdf850111::FileDescriptorsOrderedByName130   inline bool operator()(const FileDescriptor* a,
131                          const FileDescriptor* b) const {
132     return a->name() < b->name();
133   }
134 };
135 
136 }  // namespace
137 
CommonState()138 FileGenerator::CommonState::CommonState() { }
139 
140 const FileGenerator::CommonState::MinDepsEntry&
CollectMinimalFileDepsContainingExtensionsInternal(const FileDescriptor * file)141 FileGenerator::CommonState::CollectMinimalFileDepsContainingExtensionsInternal(
142     const FileDescriptor* file) {
143   auto it = deps_info_cache_.find(file);
144   if (it != deps_info_cache_.end()) {
145     return it->second;
146   }
147 
148   std::set<const FileDescriptor*> min_deps_collector;
149   std::set<const FileDescriptor*> covered_deps_collector;
150   std::set<const FileDescriptor*> to_prune;
151   for (int i = 0; i < file->dependency_count(); i++) {
152     const FileDescriptor* dep = file->dependency(i);
153     MinDepsEntry dep_info =
154         CollectMinimalFileDepsContainingExtensionsInternal(dep);
155 
156     // Everything the dep covered, this file will also cover.
157     covered_deps_collector.insert(dep_info.covered_deps.begin(), dep_info.covered_deps.end());
158     // Prune everything from the dep's covered list in case another dep lists it
159     // as a min dep.
160     to_prune.insert(dep_info.covered_deps.begin(), dep_info.covered_deps.end());
161 
162     // Does the dep have any extensions...
163     if (dep_info.has_extensions) {
164       // Yes -> Add this file, prune its min_deps and add them to the covered deps.
165       min_deps_collector.insert(dep);
166       to_prune.insert(dep_info.min_deps.begin(), dep_info.min_deps.end());
167       covered_deps_collector.insert(dep_info.min_deps.begin(), dep_info.min_deps.end());
168     } else {
169       // No -> Just use its min_deps.
170       min_deps_collector.insert(dep_info.min_deps.begin(), dep_info.min_deps.end());
171     }
172   }
173 
174   const bool file_has_exts = FileContainsExtensions(file);
175 
176   // Fast path: if nothing to prune or there was only one dep, the prune work is
177   // a waste, skip it.
178   if (to_prune.empty() || file->dependency_count() == 1) {
179     return deps_info_cache_.insert(
180         {file, {file_has_exts, min_deps_collector, covered_deps_collector}}).first->second;
181   }
182 
183   std::set<const FileDescriptor*> min_deps;
184   std::copy_if(min_deps_collector.begin(), min_deps_collector.end(),
185                std::inserter(min_deps, min_deps.end()),
186                [&](const FileDescriptor* value){
187     return to_prune.find(value) == to_prune.end();
188   });
189   return deps_info_cache_.insert(
190       {file, {file_has_exts, min_deps, covered_deps_collector}}).first->second;
191 }
192 
193 // Collect the deps of the given file that contain extensions. This can be used to
194 // create the chain of roots that need to be wired together.
195 //
196 // NOTE: If any changes are made to this and the supporting functions, you will
197 // need to manually validate what the generated code is for the test files:
198 //   objectivec/Tests/unittest_extension_chain_*.proto
199 // There are comments about what the expected code should be line and limited
200 // testing objectivec/Tests/GPBUnittestProtos2.m around compilation (#imports
201 // specifically).
202 const std::vector<const FileDescriptor*>
CollectMinimalFileDepsContainingExtensions(const FileDescriptor * file)203 FileGenerator::CommonState::CollectMinimalFileDepsContainingExtensions(
204     const FileDescriptor* file) {
205   std::set<const FileDescriptor*> min_deps =
206     CollectMinimalFileDepsContainingExtensionsInternal(file).min_deps;
207   // Sort the list since pointer order isn't stable across runs.
208   std::vector<const FileDescriptor*> result(min_deps.begin(), min_deps.end());
209   std::sort(result.begin(), result.end(), FileDescriptorsOrderedByName());
210   return result;
211 }
212 
FileGenerator(const FileDescriptor * file,const GenerationOptions & generation_options,CommonState & common_state)213 FileGenerator::FileGenerator(const FileDescriptor* file,
214                              const GenerationOptions& generation_options,
215                              CommonState& common_state)
216     : file_(file),
217       generation_options_(generation_options),
218       common_state_(common_state),
219       root_class_name_(FileClassName(file)),
220       is_bundled_proto_(IsProtobufLibraryBundledProtoFile(file)) {
221   for (int i = 0; i < file_->enum_type_count(); i++) {
222     EnumGenerator* generator = new EnumGenerator(file_->enum_type(i));
223     enum_generators_.emplace_back(generator);
224   }
225   for (int i = 0; i < file_->message_type_count(); i++) {
226     MessageGenerator* generator =
227         new MessageGenerator(root_class_name_, file_->message_type(i));
228     message_generators_.emplace_back(generator);
229   }
230   for (int i = 0; i < file_->extension_count(); i++) {
231     ExtensionGenerator* generator =
232         new ExtensionGenerator(root_class_name_, file_->extension(i));
233     extension_generators_.emplace_back(generator);
234   }
235 }
236 
~FileGenerator()237 FileGenerator::~FileGenerator() {}
238 
GenerateHeader(io::Printer * printer)239 void FileGenerator::GenerateHeader(io::Printer* printer) {
240   std::vector<std::string> headers;
241   // Generated files bundled with the library get minimal imports, everything
242   // else gets the wrapper so everything is usable.
243   if (is_bundled_proto_) {
244     headers.push_back("GPBDescriptor.h");
245     headers.push_back("GPBMessage.h");
246     headers.push_back("GPBRootObject.h");
247     for (int i = 0; i < file_->dependency_count(); i++) {
248       const std::string header_name = BundledFileName(file_->dependency(i));
249       headers.push_back(header_name);
250     }
251   } else {
252     headers.push_back("GPBProtocolBuffers.h");
253   }
254   PrintFileRuntimePreamble(printer, headers);
255 
256   // Add some verification that the generated code matches the source the
257   // code is being compiled with.
258   // NOTE: This captures the raw numeric values at the time the generator was
259   // compiled, since that will be the versions for the ObjC runtime at that
260   // time.  The constants in the generated code will then get their values at
261   // at compile time (so checking against the headers being used to compile).
262   printer->Print(
263       "#if GOOGLE_PROTOBUF_OBJC_VERSION < $google_protobuf_objc_version$\n"
264       "#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.\n"
265       "#endif\n"
266       "#if $google_protobuf_objc_version$ < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION\n"
267       "#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.\n"
268       "#endif\n"
269       "\n",
270       "google_protobuf_objc_version", StrCat(GOOGLE_PROTOBUF_OBJC_VERSION));
271 
272   // The bundled protos (WKTs) don't use of forward declarations.
273   bool headers_use_forward_declarations =
274       generation_options_.headers_use_forward_declarations && !is_bundled_proto_;
275 
276   {
277     ImportWriter import_writer(
278         generation_options_.generate_for_named_framework,
279         generation_options_.named_framework_to_proto_path_mappings_path,
280         generation_options_.runtime_import_prefix,
281         /* include_wkt_imports = */ false);
282     const std::string header_extension(kHeaderExtension);
283     if (headers_use_forward_declarations) {
284       // #import any headers for "public imports" in the proto file.
285       for (int i = 0; i < file_->public_dependency_count(); i++) {
286         import_writer.AddFile(file_->public_dependency(i), header_extension);
287       }
288     } else {
289       for (int i = 0; i < file_->dependency_count(); i++) {
290         import_writer.AddFile(file_->dependency(i), header_extension);
291       }
292     }
293     import_writer.Print(printer);
294   }
295 
296   // Note:
297   //  deprecated-declarations suppression is only needed if some place in this
298   //    proto file is something deprecated or if it references something from
299   //    another file that is deprecated.
300   printer->Print(
301       "// @@protoc_insertion_point(imports)\n"
302       "\n"
303       "#pragma clang diagnostic push\n"
304       "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"
305       "\n"
306       "CF_EXTERN_C_BEGIN\n"
307       "\n");
308 
309   std::set<std::string> fwd_decls;
310   for (const auto& generator : message_generators_) {
311     generator->DetermineForwardDeclarations(
312         &fwd_decls,
313         /* include_external_types = */ headers_use_forward_declarations);
314   }
315   for (std::set<std::string>::const_iterator i(fwd_decls.begin());
316        i != fwd_decls.end(); ++i) {
317     printer->Print("$value$;\n", "value", *i);
318   }
319   if (fwd_decls.begin() != fwd_decls.end()) {
320     printer->Print("\n");
321   }
322 
323   printer->Print(
324       "NS_ASSUME_NONNULL_BEGIN\n"
325       "\n");
326 
327   // need to write out all enums first
328   for (const auto& generator : enum_generators_) {
329     generator->GenerateHeader(printer);
330   }
331 
332   for (const auto& generator : message_generators_) {
333     generator->GenerateEnumHeader(printer);
334   }
335 
336   // For extensions to chain together, the Root gets created even if there
337   // are no extensions.
338   printer->Print(
339       "#pragma mark - $root_class_name$\n"
340       "\n"
341       "/**\n"
342       " * Exposes the extension registry for this file.\n"
343       " *\n"
344       " * The base class provides:\n"
345       " * @code\n"
346       " *   + (GPBExtensionRegistry *)extensionRegistry;\n"
347       " * @endcode\n"
348       " * which is a @c GPBExtensionRegistry that includes all the extensions defined by\n"
349       " * this file and all files that it depends on.\n"
350       " **/\n"
351       "GPB_FINAL @interface $root_class_name$ : GPBRootObject\n"
352       "@end\n"
353       "\n",
354       "root_class_name", root_class_name_);
355 
356   if (!extension_generators_.empty()) {
357     // The dynamic methods block is only needed if there are extensions.
358     printer->Print(
359         "@interface $root_class_name$ (DynamicMethods)\n",
360         "root_class_name", root_class_name_);
361 
362     for (const auto& generator : extension_generators_) {
363       generator->GenerateMembersHeader(printer);
364     }
365 
366     printer->Print("@end\n\n");
367   }  // !extension_generators_.empty()
368 
369   for (const auto& generator : message_generators_) {
370     generator->GenerateMessageHeader(printer);
371   }
372 
373   printer->Print(
374       "NS_ASSUME_NONNULL_END\n"
375       "\n"
376       "CF_EXTERN_C_END\n"
377       "\n"
378       "#pragma clang diagnostic pop\n"
379       "\n"
380       "// @@protoc_insertion_point(global_scope)\n");
381 }
382 
GenerateSource(io::Printer * printer)383 void FileGenerator::GenerateSource(io::Printer* printer) {
384   // #import the runtime support.
385   std::vector<std::string> headers;
386   headers.push_back("GPBProtocolBuffers_RuntimeSupport.h");
387   if (is_bundled_proto_) {
388     headers.push_back(BundledFileName(file_));
389   }
390   PrintFileRuntimePreamble(printer, headers);
391 
392   // Enums use atomic in the generated code, so add the system import as needed.
393   if (FileContainsEnums(file_)) {
394     printer->Print(
395         "#import <stdatomic.h>\n"
396         "\n");
397   }
398 
399   std::vector<const FileDescriptor*> deps_with_extensions =
400     common_state_.CollectMinimalFileDepsContainingExtensions(file_);
401 
402   // The bundled protos (WKTs) don't use of forward declarations.
403   bool headers_use_forward_declarations =
404       generation_options_.headers_use_forward_declarations && !is_bundled_proto_;
405 
406   {
407     ImportWriter import_writer(
408         generation_options_.generate_for_named_framework,
409         generation_options_.named_framework_to_proto_path_mappings_path,
410         generation_options_.runtime_import_prefix,
411         /* include_wkt_imports = */ false);
412     const std::string header_extension(kHeaderExtension);
413 
414     // #import the header for this proto file.
415     import_writer.AddFile(file_, header_extension);
416 
417     if (headers_use_forward_declarations) {
418       // #import the headers for anything that a plain dependency of this proto
419       // file (that means they were just an include, not a "public" include).
420       std::set<std::string> public_import_names;
421       for (int i = 0; i < file_->public_dependency_count(); i++) {
422         public_import_names.insert(file_->public_dependency(i)->name());
423       }
424       for (int i = 0; i < file_->dependency_count(); i++) {
425         const FileDescriptor *dep = file_->dependency(i);
426         bool public_import = (public_import_names.count(dep->name()) != 0);
427         if (!public_import) {
428           import_writer.AddFile(dep, header_extension);
429         }
430       }
431     }
432 
433     // If any indirect dependency provided extensions, it needs to be directly
434     // imported so it can get merged into the root's extensions registry.
435     // See the Note by CollectMinimalFileDepsContainingExtensions before
436     // changing this.
437     for (std::vector<const FileDescriptor*>::iterator iter =
438              deps_with_extensions.begin();
439          iter != deps_with_extensions.end(); ++iter) {
440       if (!IsDirectDependency(*iter, file_)) {
441         import_writer.AddFile(*iter, header_extension);
442       }
443     }
444 
445     import_writer.Print(printer);
446   }
447 
448   bool includes_oneof = false;
449   for (const auto& generator : message_generators_) {
450     if (generator->IncludesOneOfDefinition()) {
451       includes_oneof = true;
452       break;
453     }
454   }
455 
456   std::set<std::string> fwd_decls;
457   for (const auto& generator : message_generators_) {
458     generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
459   }
460   for (const auto& generator : extension_generators_) {
461     generator->DetermineObjectiveCClassDefinitions(&fwd_decls);
462   }
463 
464   // Note:
465   //  deprecated-declarations suppression is only needed if some place in this
466   //    proto file is something deprecated or if it references something from
467   //    another file that is deprecated.
468   //  dollar-in-identifier-extension is needed because we use references to
469   //    objc class names that have $ in identifiers.
470   printer->Print(
471       "// @@protoc_insertion_point(imports)\n"
472       "\n"
473       "#pragma clang diagnostic push\n"
474       "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n");
475   if (includes_oneof) {
476     // The generated code for oneof's uses direct ivar access, suppress the
477     // warning in case developer turn that on in the context they compile the
478     // generated code.
479     printer->Print(
480         "#pragma clang diagnostic ignored \"-Wdirect-ivar-access\"\n");
481   }
482   if (!fwd_decls.empty()) {
483     printer->Print(
484       "#pragma clang diagnostic ignored \"-Wdollar-in-identifier-extension\"\n");
485   }
486   printer->Print(
487       "\n");
488   if (!fwd_decls.empty()) {
489     printer->Print(
490         "#pragma mark - Objective C Class declarations\n"
491         "// Forward declarations of Objective C classes that we can use as\n"
492         "// static values in struct initializers.\n"
493         "// We don't use [Foo class] because it is not a static value.\n");
494   }
495   for (const auto& i : fwd_decls) {
496     printer->Print("$value$\n", "value", i);
497   }
498   if (!fwd_decls.empty()) {
499     printer->Print("\n");
500   }
501   printer->Print(
502       "#pragma mark - $root_class_name$\n"
503       "\n"
504       "@implementation $root_class_name$\n\n",
505       "root_class_name", root_class_name_);
506 
507   const bool file_contains_extensions = FileContainsExtensions(file_);
508 
509   // If there were any extensions or this file has any dependencies, output
510   // a registry to override to create the file specific registry.
511   if (file_contains_extensions || !deps_with_extensions.empty()) {
512     printer->Print(
513         "+ (GPBExtensionRegistry*)extensionRegistry {\n"
514         "  // This is called by +initialize so there is no need to worry\n"
515         "  // about thread safety and initialization of registry.\n"
516         "  static GPBExtensionRegistry* registry = nil;\n"
517         "  if (!registry) {\n"
518         "    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n"
519         "    registry = [[GPBExtensionRegistry alloc] init];\n");
520 
521     printer->Indent();
522     printer->Indent();
523 
524     if (file_contains_extensions) {
525       printer->Print(
526           "static GPBExtensionDescription descriptions[] = {\n");
527       printer->Indent();
528       for (const auto& generator : extension_generators_) {
529         generator->GenerateStaticVariablesInitialization(printer);
530       }
531       for (const auto& generator : message_generators_) {
532         generator->GenerateStaticVariablesInitialization(printer);
533       }
534       printer->Outdent();
535       printer->Print(
536           "};\n"
537           "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n"
538           "  GPBExtensionDescriptor *extension =\n"
539           "      [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]\n"
540           "                                                     usesClassRefs:YES];\n"
541           "  [registry addExtension:extension];\n"
542           "  [self globallyRegisterExtension:extension];\n"
543           "  [extension release];\n"
544           "}\n");
545     }
546 
547     if (deps_with_extensions.empty()) {
548       printer->Print(
549           "// None of the imports (direct or indirect) defined extensions, so no need to add\n"
550           "// them to this registry.\n");
551     } else {
552       printer->Print(
553           "// Merge in the imports (direct or indirect) that defined extensions.\n");
554       for (std::vector<const FileDescriptor*>::iterator iter =
555                deps_with_extensions.begin();
556            iter != deps_with_extensions.end(); ++iter) {
557         const std::string root_class_name(FileClassName((*iter)));
558         printer->Print(
559             "[registry addExtensions:[$dependency$ extensionRegistry]];\n",
560             "dependency", root_class_name);
561       }
562     }
563 
564     printer->Outdent();
565     printer->Outdent();
566 
567     printer->Print(
568         "  }\n"
569         "  return registry;\n"
570         "}\n");
571   } else {
572     if (file_->dependency_count() > 0) {
573       printer->Print(
574           "// No extensions in the file and none of the imports (direct or indirect)\n"
575           "// defined extensions, so no need to generate +extensionRegistry.\n");
576     } else {
577       printer->Print(
578           "// No extensions in the file and no imports, so no need to generate\n"
579           "// +extensionRegistry.\n");
580     }
581   }
582 
583   printer->Print("\n@end\n\n");
584 
585   // File descriptor only needed if there are messages to use it.
586   if (!message_generators_.empty()) {
587     std::map<std::string, std::string> vars;
588     vars["root_class_name"] = root_class_name_;
589     vars["package"] = file_->package();
590     vars["objc_prefix"] = FileClassPrefix(file_);
591     switch (file_->syntax()) {
592       case FileDescriptor::SYNTAX_UNKNOWN:
593         vars["syntax"] = "GPBFileSyntaxUnknown";
594         break;
595       case FileDescriptor::SYNTAX_PROTO2:
596         vars["syntax"] = "GPBFileSyntaxProto2";
597         break;
598       case FileDescriptor::SYNTAX_PROTO3:
599         vars["syntax"] = "GPBFileSyntaxProto3";
600         break;
601     }
602     printer->Print(vars,
603         "#pragma mark - $root_class_name$_FileDescriptor\n"
604         "\n"
605         "static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n"
606         "  // This is called by +initialize so there is no need to worry\n"
607         "  // about thread safety of the singleton.\n"
608         "  static GPBFileDescriptor *descriptor = NULL;\n"
609         "  if (!descriptor) {\n"
610         "    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n");
611     if (!vars["objc_prefix"].empty()) {
612       printer->Print(
613           vars,
614           "    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
615           "                                                 objcPrefix:@\"$objc_prefix$\"\n"
616           "                                                     syntax:$syntax$];\n");
617     } else {
618       printer->Print(
619           vars,
620           "    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
621           "                                                     syntax:$syntax$];\n");
622     }
623     printer->Print(
624         "  }\n"
625         "  return descriptor;\n"
626         "}\n"
627         "\n");
628   }
629 
630   for (const auto& generator : enum_generators_) {
631     generator->GenerateSource(printer);
632   }
633   for (const auto& generator : message_generators_) {
634     generator->GenerateSource(printer);
635   }
636 
637   printer->Print(
638     "\n"
639     "#pragma clang diagnostic pop\n"
640     "\n"
641     "// @@protoc_insertion_point(global_scope)\n");
642 }
643 
644 // Helper to print the import of the runtime support at the top of generated
645 // files. This currently only supports the runtime coming from a framework
646 // as defined by the official CocoaPod.
PrintFileRuntimePreamble(io::Printer * printer,const std::vector<std::string> & headers_to_import) const647 void FileGenerator::PrintFileRuntimePreamble(
648     io::Printer* printer,
649     const std::vector<std::string>& headers_to_import) const {
650   printer->Print(
651       "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
652       "// source: $filename$\n"
653       "\n",
654       "filename", file_->name());
655 
656   if (is_bundled_proto_) {
657     // This is basically a clone of ImportWriter::PrintRuntimeImports() but
658     // without the CPP symbol gate, since within the bundled files, that isn't
659     // needed.
660     std::string import_prefix = generation_options_.runtime_import_prefix;
661     if (!import_prefix.empty()) {
662       import_prefix += "/";
663     }
664     for (const auto& header : headers_to_import) {
665       printer->Print(
666           "#import \"$import_prefix$$header$\"\n",
667           "import_prefix", import_prefix,
668           "header", header);
669     }
670   } else {
671     ImportWriter::PrintRuntimeImports(
672         printer, headers_to_import, generation_options_.runtime_import_prefix, true);
673   }
674 
675   printer->Print("\n");
676 }
677 
678 }  // namespace objectivec
679 }  // namespace compiler
680 }  // namespace protobuf
681 }  // namespace google
682