• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_message.h>
35 #include <google/protobuf/compiler/code_generator.h>
36 #include <google/protobuf/io/printer.h>
37 #include <google/protobuf/io/zero_copy_stream_impl.h>
38 #include <google/protobuf/stubs/stl_util.h>
39 #include <google/protobuf/stubs/strutil.h>
40 #include <algorithm> // std::find()
41 #include <iostream>
42 #include <sstream>
43 
44 // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
45 // error cases, so it seems to be ok to use as a back door for errors.
46 
47 namespace google {
48 namespace protobuf {
49 namespace compiler {
50 namespace objectivec {
51 
52 namespace {
53 
54 // This is also found in GPBBootstrap.h, and needs to be kept in sync.
55 const int32 GOOGLE_PROTOBUF_OBJC_VERSION = 30002;
56 
57 const char* kHeaderExtension = ".pbobjc.h";
58 
59 // Checks if a message contains any enums definitions (on the message or
60 // a nested message under it).
MessageContainsEnums(const Descriptor * message)61 bool MessageContainsEnums(const Descriptor* message) {
62   if (message->enum_type_count() > 0) {
63     return true;
64   }
65   for (int i = 0; i < message->nested_type_count(); i++) {
66     if (MessageContainsEnums(message->nested_type(i))) {
67       return true;
68     }
69   }
70   return false;
71 }
72 
73 // Checks if a message contains any extension definitions (on the message or
74 // a nested message under it).
MessageContainsExtensions(const Descriptor * message)75 bool MessageContainsExtensions(const Descriptor* message) {
76   if (message->extension_count() > 0) {
77     return true;
78   }
79   for (int i = 0; i < message->nested_type_count(); i++) {
80     if (MessageContainsExtensions(message->nested_type(i))) {
81       return true;
82     }
83   }
84   return false;
85 }
86 
87 // Checks if the file contains any enum definitions (at the root or
88 // nested under a message).
FileContainsEnums(const FileDescriptor * file)89 bool FileContainsEnums(const FileDescriptor* file) {
90   if (file->enum_type_count() > 0) {
91     return true;
92   }
93   for (int i = 0; i < file->message_type_count(); i++) {
94     if (MessageContainsEnums(file->message_type(i))) {
95       return true;
96     }
97   }
98   return false;
99 }
100 
101 // Checks if the file contains any extensions definitions (at the root or
102 // nested under a message).
FileContainsExtensions(const FileDescriptor * file)103 bool FileContainsExtensions(const FileDescriptor* file) {
104   if (file->extension_count() > 0) {
105     return true;
106   }
107   for (int i = 0; i < file->message_type_count(); i++) {
108     if (MessageContainsExtensions(file->message_type(i))) {
109       return true;
110     }
111   }
112   return false;
113 }
114 
115 // Helper for CollectMinimalFileDepsContainingExtensionsWorker that marks all
116 // deps as visited and prunes them from the needed files list.
PruneFileAndDepsMarkingAsVisited(const FileDescriptor * file,std::vector<const FileDescriptor * > * files,std::set<const FileDescriptor * > * files_visited)117 void PruneFileAndDepsMarkingAsVisited(
118     const FileDescriptor* file,
119     std::vector<const FileDescriptor*>* files,
120     std::set<const FileDescriptor*>* files_visited) {
121   std::vector<const FileDescriptor*>::iterator iter =
122       std::find(files->begin(), files->end(), file);
123   if (iter != files->end()) {
124     files->erase(iter);
125   }
126   files_visited->insert(file);
127   for (int i = 0; i < file->dependency_count(); i++) {
128     PruneFileAndDepsMarkingAsVisited(file->dependency(i), files, files_visited);
129   }
130 }
131 
132 // Helper for CollectMinimalFileDepsContainingExtensions.
CollectMinimalFileDepsContainingExtensionsWorker(const FileDescriptor * file,std::vector<const FileDescriptor * > * files,std::set<const FileDescriptor * > * files_visited)133 void CollectMinimalFileDepsContainingExtensionsWorker(
134     const FileDescriptor* file,
135     std::vector<const FileDescriptor*>* files,
136     std::set<const FileDescriptor*>* files_visited) {
137   if (files_visited->find(file) != files_visited->end()) {
138     return;
139   }
140   files_visited->insert(file);
141 
142   if (FileContainsExtensions(file)) {
143     files->push_back(file);
144     for (int i = 0; i < file->dependency_count(); i++) {
145       const FileDescriptor* dep = file->dependency(i);
146       PruneFileAndDepsMarkingAsVisited(dep, files, files_visited);
147     }
148   } else {
149     for (int i = 0; i < file->dependency_count(); i++) {
150       const FileDescriptor* dep = file->dependency(i);
151       CollectMinimalFileDepsContainingExtensionsWorker(dep, files,
152                                                        files_visited);
153     }
154   }
155 }
156 
157 // Collect the deps of the given file that contain extensions. This can be used to
158 // create the chain of roots that need to be wired together.
159 //
160 // NOTE: If any changes are made to this and the supporting functions, you will
161 // need to manually validate what the generated code is for the test files:
162 //   objectivec/Tests/unittest_extension_chain_*.proto
163 // There are comments about what the expected code should be line and limited
164 // testing objectivec/Tests/GPBUnittestProtos2.m around compilation (#imports
165 // specifically).
CollectMinimalFileDepsContainingExtensions(const FileDescriptor * file,std::vector<const FileDescriptor * > * files)166 void CollectMinimalFileDepsContainingExtensions(
167     const FileDescriptor* file,
168     std::vector<const FileDescriptor*>* files) {
169   std::set<const FileDescriptor*> files_visited;
170   for (int i = 0; i < file->dependency_count(); i++) {
171     const FileDescriptor* dep = file->dependency(i);
172     CollectMinimalFileDepsContainingExtensionsWorker(dep, files,
173                                                      &files_visited);
174   }
175 }
176 
IsDirectDependency(const FileDescriptor * dep,const FileDescriptor * file)177 bool IsDirectDependency(const FileDescriptor* dep, const FileDescriptor* file) {
178   for (int i = 0; i < file->dependency_count(); i++) {
179     if (dep == file->dependency(i)) {
180       return true;
181     }
182   }
183   return false;
184 }
185 
186 }  // namespace
187 
FileGenerator(const FileDescriptor * file,const Options & options)188 FileGenerator::FileGenerator(const FileDescriptor *file, const Options& options)
189     : file_(file),
190       root_class_name_(FileClassName(file)),
191       is_bundled_proto_(IsProtobufLibraryBundledProtoFile(file)),
192       options_(options) {
193   for (int i = 0; i < file_->enum_type_count(); i++) {
194     EnumGenerator *generator = new EnumGenerator(file_->enum_type(i));
195     enum_generators_.emplace_back(generator);
196   }
197   for (int i = 0; i < file_->message_type_count(); i++) {
198     MessageGenerator *generator =
199         new MessageGenerator(root_class_name_, file_->message_type(i), options_);
200     message_generators_.emplace_back(generator);
201   }
202   for (int i = 0; i < file_->extension_count(); i++) {
203     ExtensionGenerator *generator =
204         new ExtensionGenerator(root_class_name_, file_->extension(i));
205     extension_generators_.emplace_back(generator);
206   }
207 }
208 
~FileGenerator()209 FileGenerator::~FileGenerator() {}
210 
GenerateHeader(io::Printer * printer)211 void FileGenerator::GenerateHeader(io::Printer *printer) {
212   std::set<string> headers;
213   // Generated files bundled with the library get minimal imports, everything
214   // else gets the wrapper so everything is usable.
215   if (is_bundled_proto_) {
216     headers.insert("GPBRootObject.h");
217     headers.insert("GPBMessage.h");
218     headers.insert("GPBDescriptor.h");
219   } else {
220     headers.insert("GPBProtocolBuffers.h");
221   }
222   PrintFileRuntimePreamble(printer, headers);
223 
224   // Add some verification that the generated code matches the source the
225   // code is being compiled with.
226   // NOTE: This captures the raw numeric values at the time the generator was
227   // compiled, since that will be the versions for the ObjC runtime at that
228   // time.  The constants in the generated code will then get their values at
229   // at compile time (so checking against the headers being used to compile).
230   printer->Print(
231       "#if GOOGLE_PROTOBUF_OBJC_VERSION < $google_protobuf_objc_version$\n"
232       "#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.\n"
233       "#endif\n"
234       "#if $google_protobuf_objc_version$ < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION\n"
235       "#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.\n"
236       "#endif\n"
237       "\n",
238       "google_protobuf_objc_version", StrCat(GOOGLE_PROTOBUF_OBJC_VERSION));
239 
240   // #import any headers for "public imports" in the proto file.
241   {
242     ImportWriter import_writer(
243         options_.generate_for_named_framework,
244         options_.named_framework_to_proto_path_mappings_path,
245         is_bundled_proto_);
246     const string header_extension(kHeaderExtension);
247     for (int i = 0; i < file_->public_dependency_count(); i++) {
248       import_writer.AddFile(file_->public_dependency(i), header_extension);
249     }
250     import_writer.Print(printer);
251   }
252 
253   // Note:
254   //  deprecated-declarations suppression is only needed if some place in this
255   //    proto file is something deprecated or if it references something from
256   //    another file that is deprecated.
257   printer->Print(
258       "// @@protoc_insertion_point(imports)\n"
259       "\n"
260       "#pragma clang diagnostic push\n"
261       "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"
262       "\n"
263       "CF_EXTERN_C_BEGIN\n"
264       "\n");
265 
266   std::set<string> fwd_decls;
267   for (const auto& generator : message_generators_) {
268     generator->DetermineForwardDeclarations(&fwd_decls);
269   }
270   for (std::set<string>::const_iterator i(fwd_decls.begin());
271        i != fwd_decls.end(); ++i) {
272     printer->Print("$value$;\n", "value", *i);
273   }
274   if (fwd_decls.begin() != fwd_decls.end()) {
275     printer->Print("\n");
276   }
277 
278   printer->Print(
279       "NS_ASSUME_NONNULL_BEGIN\n"
280       "\n");
281 
282   // need to write out all enums first
283   for (const auto& generator : enum_generators_) {
284     generator->GenerateHeader(printer);
285   }
286 
287   for (const auto& generator : message_generators_) {
288     generator->GenerateEnumHeader(printer);
289   }
290 
291   // For extensions to chain together, the Root gets created even if there
292   // are no extensions.
293   printer->Print(
294       "#pragma mark - $root_class_name$\n"
295       "\n"
296       "/**\n"
297       " * Exposes the extension registry for this file.\n"
298       " *\n"
299       " * The base class provides:\n"
300       " * @code\n"
301       " *   + (GPBExtensionRegistry *)extensionRegistry;\n"
302       " * @endcode\n"
303       " * which is a @c GPBExtensionRegistry that includes all the extensions defined by\n"
304       " * this file and all files that it depends on.\n"
305       " **/\n"
306       "@interface $root_class_name$ : GPBRootObject\n"
307       "@end\n"
308       "\n",
309       "root_class_name", root_class_name_);
310 
311   if (extension_generators_.size() > 0) {
312     // The dynamic methods block is only needed if there are extensions.
313     printer->Print(
314         "@interface $root_class_name$ (DynamicMethods)\n",
315         "root_class_name", root_class_name_);
316 
317     for (const auto& generator : extension_generators_) {
318       generator->GenerateMembersHeader(printer);
319     }
320 
321     printer->Print("@end\n\n");
322   }  // extension_generators_.size() > 0
323 
324   for (const auto& generator : message_generators_) {
325     generator->GenerateMessageHeader(printer);
326   }
327 
328   printer->Print(
329       "NS_ASSUME_NONNULL_END\n"
330       "\n"
331       "CF_EXTERN_C_END\n"
332       "\n"
333       "#pragma clang diagnostic pop\n"
334       "\n"
335       "// @@protoc_insertion_point(global_scope)\n");
336 }
337 
GenerateSource(io::Printer * printer)338 void FileGenerator::GenerateSource(io::Printer *printer) {
339   // #import the runtime support.
340   std::set<string> headers;
341   headers.insert("GPBProtocolBuffers_RuntimeSupport.h");
342   PrintFileRuntimePreamble(printer, headers);
343 
344   // Enums use atomic in the generated code, so add the system import as needed.
345   if (FileContainsEnums(file_)) {
346     printer->Print(
347         "#import <stdatomic.h>\n"
348         "\n");
349   }
350 
351   std::vector<const FileDescriptor*> deps_with_extensions;
352   CollectMinimalFileDepsContainingExtensions(file_, &deps_with_extensions);
353 
354   {
355     ImportWriter import_writer(
356         options_.generate_for_named_framework,
357         options_.named_framework_to_proto_path_mappings_path,
358         is_bundled_proto_);
359     const string header_extension(kHeaderExtension);
360 
361     // #import the header for this proto file.
362     import_writer.AddFile(file_, header_extension);
363 
364     // #import the headers for anything that a plain dependency of this proto
365     // file (that means they were just an include, not a "public" include).
366     std::set<string> public_import_names;
367     for (int i = 0; i < file_->public_dependency_count(); i++) {
368       public_import_names.insert(file_->public_dependency(i)->name());
369     }
370     for (int i = 0; i < file_->dependency_count(); i++) {
371       const FileDescriptor *dep = file_->dependency(i);
372       bool public_import = (public_import_names.count(dep->name()) != 0);
373       if (!public_import) {
374         import_writer.AddFile(dep, header_extension);
375       }
376     }
377 
378     // If any indirect dependency provided extensions, it needs to be directly
379     // imported so it can get merged into the root's extensions registry.
380     // See the Note by CollectMinimalFileDepsContainingExtensions before
381     // changing this.
382     for (std::vector<const FileDescriptor *>::iterator iter =
383              deps_with_extensions.begin();
384          iter != deps_with_extensions.end(); ++iter) {
385       if (!IsDirectDependency(*iter, file_)) {
386         import_writer.AddFile(*iter, header_extension);
387       }
388     }
389 
390     import_writer.Print(printer);
391   }
392 
393   bool includes_oneof = false;
394   for (const auto& generator : message_generators_) {
395     if (generator->IncludesOneOfDefinition()) {
396       includes_oneof = true;
397       break;
398     }
399   }
400 
401   // Note:
402   //  deprecated-declarations suppression is only needed if some place in this
403   //    proto file is something deprecated or if it references something from
404   //    another file that is deprecated.
405   printer->Print(
406       "// @@protoc_insertion_point(imports)\n"
407       "\n"
408       "#pragma clang diagnostic push\n"
409       "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n");
410   if (includes_oneof) {
411     // The generated code for oneof's uses direct ivar access, suppress the
412     // warning incase developer turn that on in the context they compile the
413     // generated code.
414     printer->Print(
415         "#pragma clang diagnostic ignored \"-Wdirect-ivar-access\"\n");
416   }
417 
418   printer->Print(
419       "\n"
420       "#pragma mark - $root_class_name$\n"
421       "\n"
422       "@implementation $root_class_name$\n\n",
423       "root_class_name", root_class_name_);
424 
425   const bool file_contains_extensions = FileContainsExtensions(file_);
426 
427   // If there were any extensions or this file has any dependencies, output
428   // a registry to override to create the file specific registry.
429   if (file_contains_extensions || !deps_with_extensions.empty()) {
430     printer->Print(
431         "+ (GPBExtensionRegistry*)extensionRegistry {\n"
432         "  // This is called by +initialize so there is no need to worry\n"
433         "  // about thread safety and initialization of registry.\n"
434         "  static GPBExtensionRegistry* registry = nil;\n"
435         "  if (!registry) {\n"
436         "    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n"
437         "    registry = [[GPBExtensionRegistry alloc] init];\n");
438 
439     printer->Indent();
440     printer->Indent();
441 
442     if (file_contains_extensions) {
443       printer->Print(
444           "static GPBExtensionDescription descriptions[] = {\n");
445       printer->Indent();
446       for (const auto& generator : extension_generators_) {
447         generator->GenerateStaticVariablesInitialization(printer);
448       }
449       for (const auto& generator : message_generators_) {
450         generator->GenerateStaticVariablesInitialization(printer);
451       }
452       printer->Outdent();
453       printer->Print(
454           "};\n"
455           "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n"
456           "  GPBExtensionDescriptor *extension =\n"
457           "      [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]];\n"
458           "  [registry addExtension:extension];\n"
459           "  [self globallyRegisterExtension:extension];\n"
460           "  [extension release];\n"
461           "}\n");
462     }
463 
464     if (deps_with_extensions.empty()) {
465       printer->Print(
466           "// None of the imports (direct or indirect) defined extensions, so no need to add\n"
467           "// them to this registry.\n");
468     } else {
469       printer->Print(
470           "// Merge in the imports (direct or indirect) that defined extensions.\n");
471       for (std::vector<const FileDescriptor *>::iterator iter =
472                deps_with_extensions.begin();
473            iter != deps_with_extensions.end(); ++iter) {
474         const string root_class_name(FileClassName((*iter)));
475         printer->Print(
476             "[registry addExtensions:[$dependency$ extensionRegistry]];\n",
477             "dependency", root_class_name);
478       }
479     }
480 
481     printer->Outdent();
482     printer->Outdent();
483 
484     printer->Print(
485         "  }\n"
486         "  return registry;\n"
487         "}\n");
488   } else {
489     if (file_->dependency_count() > 0) {
490       printer->Print(
491           "// No extensions in the file and none of the imports (direct or indirect)\n"
492           "// defined extensions, so no need to generate +extensionRegistry.\n");
493     } else {
494       printer->Print(
495           "// No extensions in the file and no imports, so no need to generate\n"
496           "// +extensionRegistry.\n");
497     }
498   }
499 
500   printer->Print("\n@end\n\n");
501 
502   // File descriptor only needed if there are messages to use it.
503   if (message_generators_.size() > 0) {
504     std::map<string, string> vars;
505     vars["root_class_name"] = root_class_name_;
506     vars["package"] = file_->package();
507     vars["objc_prefix"] = FileClassPrefix(file_);
508     switch (file_->syntax()) {
509       case FileDescriptor::SYNTAX_UNKNOWN:
510         vars["syntax"] = "GPBFileSyntaxUnknown";
511         break;
512       case FileDescriptor::SYNTAX_PROTO2:
513         vars["syntax"] = "GPBFileSyntaxProto2";
514         break;
515       case FileDescriptor::SYNTAX_PROTO3:
516         vars["syntax"] = "GPBFileSyntaxProto3";
517         break;
518     }
519     printer->Print(vars,
520         "#pragma mark - $root_class_name$_FileDescriptor\n"
521         "\n"
522         "static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n"
523         "  // This is called by +initialize so there is no need to worry\n"
524         "  // about thread safety of the singleton.\n"
525         "  static GPBFileDescriptor *descriptor = NULL;\n"
526         "  if (!descriptor) {\n"
527         "    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n");
528     if (vars["objc_prefix"].size() > 0) {
529       printer->Print(
530           vars,
531           "    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
532           "                                                 objcPrefix:@\"$objc_prefix$\"\n"
533           "                                                     syntax:$syntax$];\n");
534     } else {
535       printer->Print(
536           vars,
537           "    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
538           "                                                     syntax:$syntax$];\n");
539     }
540     printer->Print(
541         "  }\n"
542         "  return descriptor;\n"
543         "}\n"
544         "\n");
545   }
546 
547   for (const auto& generator : enum_generators_) {
548     generator->GenerateSource(printer);
549   }
550   for (const auto& generator : message_generators_) {
551     generator->GenerateSource(printer);
552   }
553 
554   printer->Print(
555     "\n"
556     "#pragma clang diagnostic pop\n"
557     "\n"
558     "// @@protoc_insertion_point(global_scope)\n");
559 }
560 
561 // Helper to print the import of the runtime support at the top of generated
562 // files. This currently only supports the runtime coming from a framework
563 // as defined by the official CocoaPod.
PrintFileRuntimePreamble(io::Printer * printer,const std::set<string> & headers_to_import) const564 void FileGenerator::PrintFileRuntimePreamble(
565     io::Printer* printer, const std::set<string>& headers_to_import) const {
566   printer->Print(
567       "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
568       "// source: $filename$\n"
569       "\n",
570       "filename", file_->name());
571 
572   const string framework_name(ProtobufLibraryFrameworkName);
573   const string cpp_symbol(ProtobufFrameworkImportSymbol(framework_name));
574 
575   printer->Print(
576       "// This CPP symbol can be defined to use imports that match up to the framework\n"
577       "// imports needed when using CocoaPods.\n"
578       "#if !defined($cpp_symbol$)\n"
579       " #define $cpp_symbol$ 0\n"
580       "#endif\n"
581       "\n"
582       "#if $cpp_symbol$\n",
583       "cpp_symbol", cpp_symbol);
584 
585 
586   for (std::set<string>::const_iterator iter = headers_to_import.begin();
587        iter != headers_to_import.end(); ++iter) {
588     printer->Print(
589         " #import <$framework_name$/$header$>\n",
590         "header", *iter,
591         "framework_name", framework_name);
592   }
593 
594   printer->Print(
595       "#else\n");
596 
597   for (std::set<string>::const_iterator iter = headers_to_import.begin();
598        iter != headers_to_import.end(); ++iter) {
599     printer->Print(
600         " #import \"$header$\"\n",
601         "header", *iter);
602   }
603 
604   printer->Print(
605       "#endif\n"
606       "\n");
607 }
608 
609 }  // namespace objectivec
610 }  // namespace compiler
611 }  // namespace protobuf
612 }  // namespace google
613