// 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 #include #include #include #include #include #include #include #include #include #include // This is also found in GPBBootstrap.h, and needs to be kept in sync. It // is the version check done to ensure generated code works with the current // runtime being used. const int32_t GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30000; namespace google { namespace protobuf { namespace compiler { namespace objectivec { FileGenerator::FileGenerator(const FileDescriptor *file) : file_(file), root_class_name_(FileClassName(file)), is_filtered_(true), all_extensions_filtered_(true) { for (int i = 0; i < file_->enum_type_count(); i++) { EnumGenerator *generator = new EnumGenerator(file_->enum_type(i)); // The enums are exposed via C functions, so they will dead strip if // not used. is_filtered_ &= false; 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)); is_filtered_ &= generator->IsFiltered(); is_filtered_ &= generator->IsSubContentFiltered(); message_generators_.push_back(generator); } for (int i = 0; i < file_->extension_count(); i++) { ExtensionGenerator *generator = new ExtensionGenerator(root_class_name_, file_->extension(i)); is_filtered_ &= generator->IsFiltered(); all_extensions_filtered_ &= generator->IsFiltered(); extension_generators_.push_back(generator); } // If there is nothing in the file we filter it. } FileGenerator::~FileGenerator() { STLDeleteContainerPointers(dependency_generators_.begin(), dependency_generators_.end()); 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) { printer->Print( "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" "// source: $filename$\n" "\n", "filename", file_->name()); printer->Print("#import \"GPBProtocolBuffers.h\"\n\n"); // Add some verification that the generated code matches the source the // code is being compiled with. printer->Print( "#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != $protoc_gen_objc_version$\n" "#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.\n" "#endif\n" "\n", "protoc_gen_objc_version", SimpleItoa(GOOGLE_PROTOBUF_OBJC_GEN_VERSION)); if (!IsFiltered()) { const vector &dependency_generators = DependencyGenerators(); if (dependency_generators.size() > 0) { for (vector::const_iterator iter = dependency_generators.begin(); iter != dependency_generators.end(); ++iter) { printer->Print("#import \"$header$.pbobjc.h\"\n", "header", (*iter)->Path()); } printer->Print("\n"); } } printer->Print("CF_EXTERN_C_BEGIN\n\n"); if (!IsFiltered()) { set dependencies; DetermineDependencies(&dependencies); for (set::const_iterator i(dependencies.begin()); i != dependencies.end(); ++i) { printer->Print("$value$;\n", "value", *i); } if (dependencies.begin() != dependencies.end()) { printer->Print("\n"); } } // need to write out all enums first for (vector::iterator iter = enum_generators_.begin(); iter != enum_generators_.end(); ++iter) { (*iter)->GenerateHeader(printer); } for (vector::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. So if the entire file isn't filtered away, output it. if (!IsFiltered()) { printer->Print( "\n" "#pragma mark - $root_class_name$\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. If // they are all filtered, output the @interface as a comment so there is // something left in the header for anyone that looks. const char *root_line_prefix = ""; if (AreAllExtensionsFiltered()) { root_line_prefix = "// "; } printer->Print( "$root_line_prefix$@interface $root_class_name$ (DynamicMethods)\n", "root_class_name", root_class_name_, "root_line_prefix", root_line_prefix); for (vector::iterator iter = extension_generators_.begin(); iter != extension_generators_.end(); ++iter) { (*iter)->GenerateMembersHeader(printer); } printer->Print("$root_line_prefix$@end\n\n", "root_line_prefix", root_line_prefix); } // extension_generators_.size() > 0 for (vector::iterator iter = message_generators_.begin(); iter != message_generators_.end(); ++iter) { (*iter)->GenerateMessageHeader(printer); } printer->Print("CF_EXTERN_C_END\n"); } void DetermineDependenciesWorker(set *dependencies, set *seen_files, const string &classname, const FileDescriptor *file) { if (seen_files->find(file->name()) != seen_files->end()) { // don't infinitely recurse return; } seen_files->insert(file->name()); for (int i = 0; i < file->dependency_count(); i++) { DetermineDependenciesWorker(dependencies, seen_files, classname, file->dependency(i)); } for (int i = 0; i < file->message_type_count(); i++) { MessageGenerator(classname, file->message_type(i)) .DetermineDependencies(dependencies); } } void FileGenerator::DetermineDependencies(set *dependencies) { set seen_files; DetermineDependenciesWorker(dependencies, &seen_files, root_class_name_, file_); } void FileGenerator::GenerateSource(io::Printer *printer) { printer->Print( "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" "// source: $filename$\n" "\n", "filename", file_->name()); if (IsFiltered()) { printer->Print( "// File empty because all messages, extensions and enum have been filtered.\n" "\n" "\n" "// Dummy symbol that will be stripped but will avoid linker warnings about\n" "// no symbols in the .o form compiling this file.\n" "static int $root_class_name$_dummy __attribute__((unused,used)) = 0;\n", "root_class_name", root_class_name_); return; } printer->Print("#import \"GPBProtocolBuffers_RuntimeSupport.h\"\n\n"); string header_file = Path() + ".pbobjc.h"; printer->Print( "#import \"$header_file$\"\n" "\n" "#pragma mark - $root_class_name$\n" "\n" "@implementation $root_class_name$\n\n", "header_file", header_file, "root_class_name", root_class_name_); bool generated_extensions = false; if (file_->extension_count() + file_->message_type_count() + file_->dependency_count() > 0) { ostringstream extensions_stringstream; if (file_->extension_count() + file_->message_type_count() > 0) { io::OstreamOutputStream extensions_outputstream(&extensions_stringstream); io::Printer extensions_printer(&extensions_outputstream, '$'); extensions_printer.Print( "static GPBExtensionDescription descriptions[] = {\n"); extensions_printer.Indent(); for (vector::iterator iter = extension_generators_.begin(); iter != extension_generators_.end(); ++iter) { (*iter)->GenerateStaticVariablesInitialization( &extensions_printer, &generated_extensions, true); } for (vector::iterator iter = message_generators_.begin(); iter != message_generators_.end(); ++iter) { (*iter)->GenerateStaticVariablesInitialization(&extensions_printer, &generated_extensions); } extensions_printer.Outdent(); extensions_printer.Print("};\n"); if (generated_extensions) { extensions_printer.Print( "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n" " GPBExtensionField *extension = [[GPBExtensionField alloc] initWithDescription:&descriptions[i]];\n" " [registry addExtension:extension];\n" " [self globallyRegisterExtension:extension];\n" " [extension release];\n" "}\n"); } else { extensions_printer.Print("#pragma unused (descriptions)\n"); } const vector &dependency_generators = DependencyGenerators(); if (dependency_generators.size()) { for (vector::const_iterator iter = dependency_generators.begin(); iter != dependency_generators.end(); ++iter) { if (!(*iter)->IsFiltered()) { extensions_printer.Print( "[registry addExtensions:[$dependency$ extensionRegistry]];\n", "dependency", (*iter)->RootClassName()); generated_extensions = true; } } } else if (!generated_extensions) { extensions_printer.Print("#pragma unused (registry)\n"); } } if (generated_extensions) { 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" " registry = [[GPBExtensionRegistry alloc] init];\n"); printer->Indent(); printer->Indent(); extensions_stringstream.flush(); printer->Print(extensions_stringstream.str().c_str()); printer->Outdent(); printer->Outdent(); printer->Print( " }\n" " return registry;\n" "}\n" "\n" "+ (void)load {\n" " @autoreleasepool {\n" " [self extensionRegistry]; // Construct extension registry.\n" " }\n" "}\n\n"); } } printer->Print("@end\n\n"); string syntax; switch (file_->syntax()) { case FileDescriptor::SYNTAX_UNKNOWN: syntax = "GPBFileSyntaxUnknown"; break; case FileDescriptor::SYNTAX_PROTO2: syntax = "GPBFileSyntaxProto2"; break; case FileDescriptor::SYNTAX_PROTO3: syntax = "GPBFileSyntaxProto3"; break; } printer->Print( "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" " descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n" " syntax:$syntax$];\n" " }\n" " return descriptor;\n" "}\n" "\n", "root_class_name", root_class_name_, "package", file_->package(), "syntax", syntax); for (vector::iterator iter = enum_generators_.begin(); iter != enum_generators_.end(); ++iter) { (*iter)->GenerateSource(printer); } for (vector::iterator iter = message_generators_.begin(); iter != message_generators_.end(); ++iter) { (*iter)->GenerateSource(printer); } } const string FileGenerator::Path() const { return FilePath(file_); } const vector &FileGenerator::DependencyGenerators() { if (file_->dependency_count() != dependency_generators_.size()) { for (int i = 0; i < file_->dependency_count(); i++) { FileGenerator *generator = new FileGenerator(file_->dependency(i)); dependency_generators_.push_back(generator); } } return dependency_generators_; } } // namespace objectivec } // namespace compiler } // namespace protobuf } // namespace google