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