diff options
Diffstat (limited to 'third_party/protobuf/3.4.0/src/google/protobuf/compiler/objectivec/objectivec_file.cc')
-rw-r--r-- | third_party/protobuf/3.4.0/src/google/protobuf/compiler/objectivec/objectivec_file.cc | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/third_party/protobuf/3.4.0/src/google/protobuf/compiler/objectivec/objectivec_file.cc b/third_party/protobuf/3.4.0/src/google/protobuf/compiler/objectivec/objectivec_file.cc new file mode 100644 index 0000000000..954b26885b --- /dev/null +++ b/third_party/protobuf/3.4.0/src/google/protobuf/compiler/objectivec/objectivec_file.cc @@ -0,0 +1,564 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <google/protobuf/compiler/objectivec/objectivec_file.h> +#include <google/protobuf/compiler/objectivec/objectivec_enum.h> +#include <google/protobuf/compiler/objectivec/objectivec_extension.h> +#include <google/protobuf/compiler/objectivec/objectivec_message.h> +#include <google/protobuf/compiler/code_generator.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/stubs/stl_util.h> +#include <google/protobuf/stubs/strutil.h> +#include <algorithm> // std::find() +#include <iostream> +#include <sstream> + +// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some +// error cases, so it seems to be ok to use as a back door for errors. + +namespace google { +namespace protobuf { +namespace compiler { +namespace objectivec { + +namespace { + +// This is also found in GPBBootstrap.h, and needs to be kept in sync. +const int32 GOOGLE_PROTOBUF_OBJC_VERSION = 30002; + +const char* kHeaderExtension = ".pbobjc.h"; + +// Checks if a message contains any extension definitions (on the message or +// a nested message under it). +bool MessageContainsExtensions(const Descriptor* message) { + if (message->extension_count() > 0) { + return true; + } + for (int i = 0; i < message->nested_type_count(); i++) { + if (MessageContainsExtensions(message->nested_type(i))) { + return true; + } + } + return false; +} + +// Checks if the file contains any extensions definitions (at the root or +// nested under a message). +bool FileContainsExtensions(const FileDescriptor* file) { + if (file->extension_count() > 0) { + return true; + } + for (int i = 0; i < file->message_type_count(); i++) { + if (MessageContainsExtensions(file->message_type(i))) { + return true; + } + } + return false; +} + +// Helper for CollectMinimalFileDepsContainingExtensionsWorker that marks all +// deps as visited and prunes them from the needed files list. +void PruneFileAndDepsMarkingAsVisited( + const FileDescriptor* file, + vector<const FileDescriptor*>* files, + std::set<const FileDescriptor*>* files_visited) { + vector<const FileDescriptor*>::iterator iter = + std::find(files->begin(), files->end(), file); + if (iter != files->end()) { + files->erase(iter); + } + files_visited->insert(file); + for (int i = 0; i < file->dependency_count(); i++) { + PruneFileAndDepsMarkingAsVisited(file->dependency(i), files, files_visited); + } +} + +// Helper for CollectMinimalFileDepsContainingExtensions. +void CollectMinimalFileDepsContainingExtensionsWorker( + const FileDescriptor* file, + vector<const FileDescriptor*>* files, + std::set<const FileDescriptor*>* files_visited) { + if (files_visited->find(file) != files_visited->end()) { + return; + } + files_visited->insert(file); + + if (FileContainsExtensions(file)) { + files->push_back(file); + for (int i = 0; i < file->dependency_count(); i++) { + const FileDescriptor* dep = file->dependency(i); + PruneFileAndDepsMarkingAsVisited(dep, files, files_visited); + } + } else { + for (int i = 0; i < file->dependency_count(); i++) { + const FileDescriptor* dep = file->dependency(i); + CollectMinimalFileDepsContainingExtensionsWorker(dep, files, + files_visited); + } + } +} + +// Collect the deps of the given file that contain extensions. This can be used to +// create the chain of roots that need to be wired together. +// +// NOTE: If any changes are made to this and the supporting functions, you will +// need to manually validate what the generated code is for the test files: +// objectivec/Tests/unittest_extension_chain_*.proto +// There are comments about what the expected code should be line and limited +// testing objectivec/Tests/GPBUnittestProtos2.m around compilation (#imports +// specifically). +void CollectMinimalFileDepsContainingExtensions( + const FileDescriptor* file, + vector<const FileDescriptor*>* files) { + std::set<const FileDescriptor*> files_visited; + for (int i = 0; i < file->dependency_count(); i++) { + const FileDescriptor* dep = file->dependency(i); + CollectMinimalFileDepsContainingExtensionsWorker(dep, files, + &files_visited); + } +} + +bool IsDirectDependency(const FileDescriptor* dep, const FileDescriptor* file) { + for (int i = 0; i < file->dependency_count(); i++) { + if (dep == file->dependency(i)) { + return true; + } + } + return false; +} + +} // namespace + +FileGenerator::FileGenerator(const FileDescriptor *file, const Options& options) + : file_(file), + root_class_name_(FileClassName(file)), + options_(options) { + for (int i = 0; i < file_->enum_type_count(); i++) { + EnumGenerator *generator = new EnumGenerator(file_->enum_type(i)); + enum_generators_.push_back(generator); + } + for (int i = 0; i < file_->message_type_count(); i++) { + MessageGenerator *generator = + new MessageGenerator(root_class_name_, file_->message_type(i), options_); + message_generators_.push_back(generator); + } + for (int i = 0; i < file_->extension_count(); i++) { + ExtensionGenerator *generator = + new ExtensionGenerator(root_class_name_, file_->extension(i)); + extension_generators_.push_back(generator); + } +} + +FileGenerator::~FileGenerator() { + STLDeleteContainerPointers(enum_generators_.begin(), enum_generators_.end()); + STLDeleteContainerPointers(message_generators_.begin(), + message_generators_.end()); + STLDeleteContainerPointers(extension_generators_.begin(), + extension_generators_.end()); +} + +void FileGenerator::GenerateHeader(io::Printer *printer) { + PrintFileRuntimePreamble(printer, "GPBProtocolBuffers.h"); + + // Add some verification that the generated code matches the source the + // code is being compiled with. + // NOTE: This captures the raw numeric values at the time the generator was + // compiled, since that will be the versions for the ObjC runtime at that + // time. The constants in the generated code will then get their values at + // at compile time (so checking against the headers being used to compile). + printer->Print( + "#if GOOGLE_PROTOBUF_OBJC_VERSION < $google_protobuf_objc_version$\n" + "#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.\n" + "#endif\n" + "#if $google_protobuf_objc_version$ < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION\n" + "#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.\n" + "#endif\n" + "\n", + "google_protobuf_objc_version", SimpleItoa(GOOGLE_PROTOBUF_OBJC_VERSION)); + + // #import any headers for "public imports" in the proto file. + { + ImportWriter import_writer( + options_.generate_for_named_framework, + options_.named_framework_to_proto_path_mappings_path); + const string header_extension(kHeaderExtension); + for (int i = 0; i < file_->public_dependency_count(); i++) { + import_writer.AddFile(file_->public_dependency(i), header_extension); + } + import_writer.Print(printer); + } + + // Note: + // deprecated-declarations suppression is only needed if some place in this + // proto file is something deprecated or if it references something from + // another file that is deprecated. + printer->Print( + "// @@protoc_insertion_point(imports)\n" + "\n" + "#pragma clang diagnostic push\n" + "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n" + "\n" + "CF_EXTERN_C_BEGIN\n" + "\n"); + + std::set<string> fwd_decls; + for (vector<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->DetermineForwardDeclarations(&fwd_decls); + } + for (std::set<string>::const_iterator i(fwd_decls.begin()); + i != fwd_decls.end(); ++i) { + printer->Print("$value$;\n", "value", *i); + } + if (fwd_decls.begin() != fwd_decls.end()) { + printer->Print("\n"); + } + + printer->Print( + "NS_ASSUME_NONNULL_BEGIN\n" + "\n"); + + // need to write out all enums first + for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin(); + iter != enum_generators_.end(); ++iter) { + (*iter)->GenerateHeader(printer); + } + + for (vector<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->GenerateEnumHeader(printer); + } + + // For extensions to chain together, the Root gets created even if there + // are no extensions. + printer->Print( + "#pragma mark - $root_class_name$\n" + "\n" + "/**\n" + " * Exposes the extension registry for this file.\n" + " *\n" + " * The base class provides:\n" + " * @code\n" + " * + (GPBExtensionRegistry *)extensionRegistry;\n" + " * @endcode\n" + " * which is a @c GPBExtensionRegistry that includes all the extensions defined by\n" + " * this file and all files that it depends on.\n" + " **/\n" + "@interface $root_class_name$ : GPBRootObject\n" + "@end\n" + "\n", + "root_class_name", root_class_name_); + + if (extension_generators_.size() > 0) { + // The dynamic methods block is only needed if there are extensions. + printer->Print( + "@interface $root_class_name$ (DynamicMethods)\n", + "root_class_name", root_class_name_); + + for (vector<ExtensionGenerator *>::iterator iter = + extension_generators_.begin(); + iter != extension_generators_.end(); ++iter) { + (*iter)->GenerateMembersHeader(printer); + } + + printer->Print("@end\n\n"); + } // extension_generators_.size() > 0 + + for (vector<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->GenerateMessageHeader(printer); + } + + printer->Print( + "NS_ASSUME_NONNULL_END\n" + "\n" + "CF_EXTERN_C_END\n" + "\n" + "#pragma clang diagnostic pop\n" + "\n" + "// @@protoc_insertion_point(global_scope)\n"); +} + +void FileGenerator::GenerateSource(io::Printer *printer) { + // #import the runtime support. + PrintFileRuntimePreamble(printer, "GPBProtocolBuffers_RuntimeSupport.h"); + + vector<const FileDescriptor*> deps_with_extensions; + CollectMinimalFileDepsContainingExtensions(file_, &deps_with_extensions); + + { + ImportWriter import_writer( + options_.generate_for_named_framework, + options_.named_framework_to_proto_path_mappings_path); + const string header_extension(kHeaderExtension); + + // #import the header for this proto file. + import_writer.AddFile(file_, header_extension); + + // #import the headers for anything that a plain dependency of this proto + // file (that means they were just an include, not a "public" include). + std::set<string> public_import_names; + for (int i = 0; i < file_->public_dependency_count(); i++) { + public_import_names.insert(file_->public_dependency(i)->name()); + } + for (int i = 0; i < file_->dependency_count(); i++) { + const FileDescriptor *dep = file_->dependency(i); + bool public_import = (public_import_names.count(dep->name()) != 0); + if (!public_import) { + import_writer.AddFile(dep, header_extension); + } + } + + // If any indirect dependency provided extensions, it needs to be directly + // imported so it can get merged into the root's extensions registry. + // See the Note by CollectMinimalFileDepsContainingExtensions before + // changing this. + for (vector<const FileDescriptor *>::iterator iter = + deps_with_extensions.begin(); + iter != deps_with_extensions.end(); ++iter) { + if (!IsDirectDependency(*iter, file_)) { + import_writer.AddFile(*iter, header_extension); + } + } + + import_writer.Print(printer); + } + + bool includes_oneof = false; + for (vector<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + if ((*iter)->IncludesOneOfDefinition()) { + includes_oneof = true; + break; + } + } + + // Note: + // deprecated-declarations suppression is only needed if some place in this + // proto file is something deprecated or if it references something from + // another file that is deprecated. + printer->Print( + "// @@protoc_insertion_point(imports)\n" + "\n" + "#pragma clang diagnostic push\n" + "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"); + if (includes_oneof) { + // The generated code for oneof's uses direct ivar access, suppress the + // warning incase developer turn that on in the context they compile the + // generated code. + printer->Print( + "#pragma clang diagnostic ignored \"-Wdirect-ivar-access\"\n"); + } + + printer->Print( + "\n" + "#pragma mark - $root_class_name$\n" + "\n" + "@implementation $root_class_name$\n\n", + "root_class_name", root_class_name_); + + const bool file_contains_extensions = FileContainsExtensions(file_); + + // If there were any extensions or this file has any dependencies, output + // a registry to override to create the file specific registry. + if (file_contains_extensions || !deps_with_extensions.empty()) { + printer->Print( + "+ (GPBExtensionRegistry*)extensionRegistry {\n" + " // This is called by +initialize so there is no need to worry\n" + " // about thread safety and initialization of registry.\n" + " static GPBExtensionRegistry* registry = nil;\n" + " if (!registry) {\n" + " GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n" + " registry = [[GPBExtensionRegistry alloc] init];\n"); + + printer->Indent(); + printer->Indent(); + + if (file_contains_extensions) { + printer->Print( + "static GPBExtensionDescription descriptions[] = {\n"); + printer->Indent(); + for (vector<ExtensionGenerator *>::iterator iter = + extension_generators_.begin(); + iter != extension_generators_.end(); ++iter) { + (*iter)->GenerateStaticVariablesInitialization(printer); + } + for (vector<MessageGenerator *>::iterator iter = + message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->GenerateStaticVariablesInitialization(printer); + } + printer->Outdent(); + printer->Print( + "};\n" + "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n" + " GPBExtensionDescriptor *extension =\n" + " [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]];\n" + " [registry addExtension:extension];\n" + " [self globallyRegisterExtension:extension];\n" + " [extension release];\n" + "}\n"); + } + + if (deps_with_extensions.empty()) { + printer->Print( + "// None of the imports (direct or indirect) defined extensions, so no need to add\n" + "// them to this registry.\n"); + } else { + printer->Print( + "// Merge in the imports (direct or indirect) that defined extensions.\n"); + for (vector<const FileDescriptor *>::iterator iter = + deps_with_extensions.begin(); + iter != deps_with_extensions.end(); ++iter) { + const string root_class_name(FileClassName((*iter))); + printer->Print( + "[registry addExtensions:[$dependency$ extensionRegistry]];\n", + "dependency", root_class_name); + } + } + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + " return registry;\n" + "}\n"); + } else { + if (file_->dependency_count() > 0) { + printer->Print( + "// No extensions in the file and none of the imports (direct or indirect)\n" + "// defined extensions, so no need to generate +extensionRegistry.\n"); + } else { + printer->Print( + "// No extensions in the file and no imports, so no need to generate\n" + "// +extensionRegistry.\n"); + } + } + + printer->Print("\n@end\n\n"); + + // File descriptor only needed if there are messages to use it. + if (message_generators_.size() > 0) { + std::map<string, string> vars; + vars["root_class_name"] = root_class_name_; + vars["package"] = file_->package(); + vars["objc_prefix"] = FileClassPrefix(file_); + switch (file_->syntax()) { + case FileDescriptor::SYNTAX_UNKNOWN: + vars["syntax"] = "GPBFileSyntaxUnknown"; + break; + case FileDescriptor::SYNTAX_PROTO2: + vars["syntax"] = "GPBFileSyntaxProto2"; + break; + case FileDescriptor::SYNTAX_PROTO3: + vars["syntax"] = "GPBFileSyntaxProto3"; + break; + } + printer->Print(vars, + "#pragma mark - $root_class_name$_FileDescriptor\n" + "\n" + "static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n" + " // This is called by +initialize so there is no need to worry\n" + " // about thread safety of the singleton.\n" + " static GPBFileDescriptor *descriptor = NULL;\n" + " if (!descriptor) {\n" + " GPB_DEBUG_CHECK_RUNTIME_VERSIONS();\n"); + if (vars["objc_prefix"].size() > 0) { + printer->Print( + vars, + " descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n" + " objcPrefix:@\"$objc_prefix$\"\n" + " syntax:$syntax$];\n"); + } else { + printer->Print( + vars, + " descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n" + " syntax:$syntax$];\n"); + } + printer->Print( + " }\n" + " return descriptor;\n" + "}\n" + "\n"); + } + + for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin(); + iter != enum_generators_.end(); ++iter) { + (*iter)->GenerateSource(printer); + } + for (vector<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->GenerateSource(printer); + } + + printer->Print( + "\n" + "#pragma clang diagnostic pop\n" + "\n" + "// @@protoc_insertion_point(global_scope)\n"); +} + +// Helper to print the import of the runtime support at the top of generated +// files. This currently only supports the runtime coming from a framework +// as defined by the official CocoaPod. +void FileGenerator::PrintFileRuntimePreamble( + io::Printer* printer, const string& header_to_import) const { + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n", + "filename", file_->name()); + + const string framework_name(ProtobufLibraryFrameworkName); + const string cpp_symbol(ProtobufFrameworkImportSymbol(framework_name)); + printer->Print( + "// This CPP symbol can be defined to use imports that match up to the framework\n" + "// imports needed when using CocoaPods.\n" + "#if !defined($cpp_symbol$)\n" + " #define $cpp_symbol$ 0\n" + "#endif\n" + "\n" + "#if $cpp_symbol$\n" + " #import <$framework_name$/$header$>\n" + "#else\n" + " #import \"$header$\"\n" + "#endif\n" + "\n", + "cpp_symbol", cpp_symbol, + "header", header_to_import, + "framework_name", framework_name); +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google |