| 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* = ".pbobjc.h" ; |
| 59 | |
| 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). |
| 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: message->nested_type(index: 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). |
| 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: message->nested_type(index: 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). |
| 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(message: file->message_type(index: 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). |
| 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(message: file->message_type(index: i))) { |
| 114 | return true; |
| 115 | } |
| 116 | } |
| 117 | return false; |
| 118 | } |
| 119 | |
| 120 | bool IsDirectDependency(const FileDescriptor* dep, const FileDescriptor* file) { |
| 121 | for (int i = 0; i < file->dependency_count(); i++) { |
| 122 | if (dep == file->dependency(index: i)) { |
| 123 | return true; |
| 124 | } |
| 125 | } |
| 126 | return false; |
| 127 | } |
| 128 | |
| 129 | struct FileDescriptorsOrderedByName { |
| 130 | inline bool operator()(const FileDescriptor* a, |
| 131 | const FileDescriptor* b) const { |
| 132 | return a->name() < b->name(); |
| 133 | } |
| 134 | }; |
| 135 | |
| 136 | } // namespace |
| 137 | |
| 138 | FileGenerator::CommonState::CommonState() { } |
| 139 | |
| 140 | const FileGenerator::CommonState::MinDepsEntry& |
| 141 | FileGenerator::CommonState::CollectMinimalFileDepsContainingExtensionsInternal( |
| 142 | const FileDescriptor* file) { |
| 143 | auto it = deps_info_cache_.find(x: 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(index: i); |
| 153 | MinDepsEntry dep_info = |
| 154 | CollectMinimalFileDepsContainingExtensionsInternal(file: dep); |
| 155 | |
| 156 | // Everything the dep covered, this file will also cover. |
| 157 | covered_deps_collector.insert(first: dep_info.covered_deps.begin(), last: 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(first: dep_info.covered_deps.begin(), last: 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(x: dep); |
| 166 | to_prune.insert(first: dep_info.min_deps.begin(), last: dep_info.min_deps.end()); |
| 167 | covered_deps_collector.insert(first: dep_info.min_deps.begin(), last: dep_info.min_deps.end()); |
| 168 | } else { |
| 169 | // No -> Just use its min_deps. |
| 170 | min_deps_collector.insert(first: dep_info.min_deps.begin(), last: 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 | x: {file, {.has_extensions: file_has_exts, .min_deps: min_deps_collector, .covered_deps: covered_deps_collector}}).first->second; |
| 181 | } |
| 182 | |
| 183 | std::set<const FileDescriptor*> min_deps; |
| 184 | std::copy_if(first: min_deps_collector.begin(), last: min_deps_collector.end(), |
| 185 | result: std::inserter(x&: min_deps, i: min_deps.end()), |
| 186 | pred: [&](const FileDescriptor* value){ |
| 187 | return to_prune.find(x: value) == to_prune.end(); |
| 188 | }); |
| 189 | return deps_info_cache_.insert( |
| 190 | x: {file, {.has_extensions: file_has_exts, .min_deps: min_deps, .covered_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*> |
| 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(first: result.begin(), last: result.end(), comp: FileDescriptorsOrderedByName()); |
| 210 | return result; |
| 211 | } |
| 212 | |
| 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(index: i)); |
| 223 | enum_generators_.emplace_back(args&: 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(index: i)); |
| 228 | message_generators_.emplace_back(args&: generator); |
| 229 | } |
| 230 | for (int i = 0; i < file_->extension_count(); i++) { |
| 231 | ExtensionGenerator* generator = |
| 232 | new ExtensionGenerator(root_class_name_, file_->extension(index: i)); |
| 233 | extension_generators_.emplace_back(args&: generator); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | FileGenerator::~FileGenerator() {} |
| 238 | |
| 239 | void FileGenerator::(io::Printer* printer) { |
| 240 | std::vector<std::string> ; |
| 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(x: "GPBDescriptor.h" ); |
| 245 | headers.push_back(x: "GPBMessage.h" ); |
| 246 | headers.push_back(x: "GPBRootObject.h" ); |
| 247 | for (int i = 0; i < file_->dependency_count(); i++) { |
| 248 | const std::string = BundledFileName(file: file_->dependency(index: i)); |
| 249 | headers.push_back(x: header_name); |
| 250 | } |
| 251 | } else { |
| 252 | headers.push_back(x: "GPBProtocolBuffers.h" ); |
| 253 | } |
| 254 | PrintFileRuntimePreamble(printer, headers_to_import: 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 | text: "#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 | args: "google_protobuf_objc_version" , args: StrCat(a: GOOGLE_PROTOBUF_OBJC_VERSION)); |
| 271 | |
| 272 | // The bundled protos (WKTs) don't use of forward declarations. |
| 273 | bool = |
| 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 (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: file_->public_dependency(index: i), header_extension); |
| 287 | } |
| 288 | } else { |
| 289 | for (int i = 0; i < file_->dependency_count(); i++) { |
| 290 | import_writer.AddFile(file: file_->dependency(index: 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 | text: "// @@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: &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(text: "$value$;\n" , args: "value" , args: *i); |
| 318 | } |
| 319 | if (fwd_decls.begin() != fwd_decls.end()) { |
| 320 | printer->Print(text: "\n" ); |
| 321 | } |
| 322 | |
| 323 | printer->Print( |
| 324 | text: "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 | text: "#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 | args: "root_class_name" , args: 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 | text: "@interface $root_class_name$ (DynamicMethods)\n" , |
| 360 | args: "root_class_name" , args: root_class_name_); |
| 361 | |
| 362 | for (const auto& generator : extension_generators_) { |
| 363 | generator->GenerateMembersHeader(printer); |
| 364 | } |
| 365 | |
| 366 | printer->Print(text: "@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 | text: "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 | |
| 383 | void FileGenerator::GenerateSource(io::Printer* printer) { |
| 384 | // #import the runtime support. |
| 385 | std::vector<std::string> ; |
| 386 | headers.push_back(x: "GPBProtocolBuffers_RuntimeSupport.h" ); |
| 387 | if (is_bundled_proto_) { |
| 388 | headers.push_back(x: BundledFileName(file: file_)); |
| 389 | } |
| 390 | PrintFileRuntimePreamble(printer, headers_to_import: headers); |
| 391 | |
| 392 | // Enums use atomic in the generated code, so add the system import as needed. |
| 393 | if (FileContainsEnums(file: file_)) { |
| 394 | printer->Print( |
| 395 | text: "#import <stdatomic.h>\n" |
| 396 | "\n" ); |
| 397 | } |
| 398 | |
| 399 | std::vector<const FileDescriptor*> deps_with_extensions = |
| 400 | common_state_.CollectMinimalFileDepsContainingExtensions(file: file_); |
| 401 | |
| 402 | // The bundled protos (WKTs) don't use of forward declarations. |
| 403 | bool = |
| 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 (kHeaderExtension); |
| 413 | |
| 414 | // #import the header for this proto file. |
| 415 | import_writer.AddFile(file: 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(x: file_->public_dependency(index: i)->name()); |
| 423 | } |
| 424 | for (int i = 0; i < file_->dependency_count(); i++) { |
| 425 | const FileDescriptor *dep = file_->dependency(index: i); |
| 426 | bool public_import = (public_import_names.count(x: dep->name()) != 0); |
| 427 | if (!public_import) { |
| 428 | import_writer.AddFile(file: 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(dep: *iter, file: file_)) { |
| 441 | import_writer.AddFile(file: *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: &fwd_decls); |
| 459 | } |
| 460 | for (const auto& generator : extension_generators_) { |
| 461 | generator->DetermineObjectiveCClassDefinitions(fwd_decls: &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 | text: "// @@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 | text: "#pragma clang diagnostic ignored \"-Wdirect-ivar-access\"\n" ); |
| 481 | } |
| 482 | if (!fwd_decls.empty()) { |
| 483 | printer->Print( |
| 484 | text: "#pragma clang diagnostic ignored \"-Wdollar-in-identifier-extension\"\n" ); |
| 485 | } |
| 486 | printer->Print( |
| 487 | text: "\n" ); |
| 488 | if (!fwd_decls.empty()) { |
| 489 | printer->Print( |
| 490 | text: "#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(text: "$value$\n" , args: "value" , args: i); |
| 497 | } |
| 498 | if (!fwd_decls.empty()) { |
| 499 | printer->Print(text: "\n" ); |
| 500 | } |
| 501 | printer->Print( |
| 502 | text: "#pragma mark - $root_class_name$\n" |
| 503 | "\n" |
| 504 | "@implementation $root_class_name$\n\n" , |
| 505 | args: "root_class_name" , args: root_class_name_); |
| 506 | |
| 507 | const bool file_contains_extensions = FileContainsExtensions(file: 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 | text: "+ (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 | text: "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 | text: "};\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 | text: "// 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 | text: "// 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(file: (*iter))); |
| 558 | printer->Print( |
| 559 | text: "[registry addExtensions:[$dependency$ extensionRegistry]];\n" , |
| 560 | args: "dependency" , args: root_class_name); |
| 561 | } |
| 562 | } |
| 563 | |
| 564 | printer->Outdent(); |
| 565 | printer->Outdent(); |
| 566 | |
| 567 | printer->Print( |
| 568 | text: " }\n" |
| 569 | " return registry;\n" |
| 570 | "}\n" ); |
| 571 | } else { |
| 572 | if (file_->dependency_count() > 0) { |
| 573 | printer->Print( |
| 574 | text: "// 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 | text: "// No extensions in the file and no imports, so no need to generate\n" |
| 579 | "// +extensionRegistry.\n" ); |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | printer->Print(text: "\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: 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(variables: vars, |
| 603 | text: "#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 | variables: vars, |
| 614 | text: " descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n" |
| 615 | " objcPrefix:@\"$objc_prefix$\"\n" |
| 616 | " syntax:$syntax$];\n" ); |
| 617 | } else { |
| 618 | printer->Print( |
| 619 | variables: vars, |
| 620 | text: " descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n" |
| 621 | " syntax:$syntax$];\n" ); |
| 622 | } |
| 623 | printer->Print( |
| 624 | text: " }\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 | text: "\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. |
| 647 | void FileGenerator::PrintFileRuntimePreamble( |
| 648 | io::Printer* printer, |
| 649 | const std::vector<std::string>& ) const { |
| 650 | printer->Print( |
| 651 | text: "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" |
| 652 | "// source: $filename$\n" |
| 653 | "\n" , |
| 654 | args: "filename" , args: 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& : headers_to_import) { |
| 665 | printer->Print( |
| 666 | text: "#import \"$import_prefix$$header$\"\n" , |
| 667 | args: "import_prefix" , args: import_prefix, |
| 668 | args: "header" , args: header); |
| 669 | } |
| 670 | } else { |
| 671 | ImportWriter::PrintRuntimeImports( |
| 672 | printer, header_to_import: headers_to_import, runtime_import_prefix: generation_options_.runtime_import_prefix, default_cpp_symbol: true); |
| 673 | } |
| 674 | |
| 675 | printer->Print(text: "\n" ); |
| 676 | } |
| 677 | |
| 678 | } // namespace objectivec |
| 679 | } // namespace compiler |
| 680 | } // namespace protobuf |
| 681 | } // namespace google |
| 682 | |