diff options
Diffstat (limited to 'src/google/protobuf/compiler/objectivec/objectivec_file.cc')
-rw-r--r-- | src/google/protobuf/compiler/objectivec/objectivec_file.cc | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.cc b/src/google/protobuf/compiler/objectivec/objectivec_file.cc new file mode 100644 index 00000000..d04eee85 --- /dev/null +++ b/src/google/protobuf/compiler/objectivec/objectivec_file.cc @@ -0,0 +1,409 @@ +// 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/descriptor.pb.h> +#include <google/protobuf/stubs/stl_util.h> +#include <google/protobuf/stubs/strutil.h> +#include <sstream> + +namespace google { +namespace protobuf { + +// 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 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30000; + +namespace compiler { +namespace objectivec { + +FileGenerator::FileGenerator(const FileDescriptor *file) + : file_(file), + root_class_name_(FileClassName(file)), + is_filtered_(true), + all_extensions_filtered_(true), + is_public_dep_(false) { + // Validate the objc prefix, do this even if the file's contents are filtered + // to catch a bad prefix as soon as it is found. + ValidateObjCClassPrefix(file_); + + 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)); + + const vector<FileGenerator *> &dependency_generators = + DependencyGenerators(); + for (vector<FileGenerator *>::const_iterator iter = + dependency_generators.begin(); + iter != dependency_generators.end(); ++iter) { + if ((*iter)->IsPublicDependency()) { + printer->Print("#import \"$header$.pbobjc.h\"\n", + "header", (*iter)->Path()); + } + } + + printer->Print( + "// @@protoc_insertion_point(imports)\n" + "\n"); + + printer->Print("CF_EXTERN_C_BEGIN\n\n"); + + if (!IsFiltered()) { + set<string> fwd_decls; + for (vector<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->DetermineForwardDeclarations(&fwd_decls); + } + for (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"); + } + } + + // 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. 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" + "\n" + "// The base class provides:\n" + "// + (GPBExtensionRegistry *)extensionRegistry;\n" + "// which is an GPBExtensionRegistry that includes all the extensions defined by\n" + "// this file and all files that it depends on.\n" + "\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<ExtensionGenerator *>::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<MessageGenerator *>::iterator iter = message_generators_.begin(); + iter != message_generators_.end(); ++iter) { + (*iter)->GenerateMessageHeader(printer); + } + + printer->Print("CF_EXTERN_C_END\n"); + + printer->Print( + "\n" + "// @@protoc_insertion_point(global_scope)\n"); +} + +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()); + + string header_file = Path() + ".pbobjc.h"; + printer->Print( + "#import \"GPBProtocolBuffers_RuntimeSupport.h\"\n" + "#import \"$header_file$\"\n", + "header_file", header_file); + const vector<FileGenerator *> &dependency_generators = + DependencyGenerators(); + for (vector<FileGenerator *>::const_iterator iter = + dependency_generators.begin(); + iter != dependency_generators.end(); ++iter) { + if (!(*iter)->IsPublicDependency()) { + printer->Print("#import \"$header$.pbobjc.h\"\n", + "header", (*iter)->Path()); + } + } + printer->Print( + "// @@protoc_insertion_point(imports)\n" + "\n"); + + 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" + "\n" + "// @@protoc_insertion_point(global_scope)\n", + "root_class_name", root_class_name_); + return; + } + + printer->Print( + "#pragma mark - $root_class_name$\n" + "\n" + "@implementation $root_class_name$\n\n", + "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<ExtensionGenerator *>::iterator iter = + extension_generators_.begin(); + iter != extension_generators_.end(); ++iter) { + (*iter)->GenerateStaticVariablesInitialization( + &extensions_printer, &generated_extensions, true); + } + for (vector<MessageGenerator *>::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<FileGenerator *> &dependency_generators = + DependencyGenerators(); + if (dependency_generators.size()) { + for (vector<FileGenerator *>::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"); + } + } + + 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<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" + "// @@protoc_insertion_point(global_scope)\n"); +} + +const string FileGenerator::Path() const { return FilePath(file_); } + +const vector<FileGenerator *> &FileGenerator::DependencyGenerators() { + if (file_->dependency_count() != dependency_generators_.size()) { + 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++) { + FileGenerator *generator = new FileGenerator(file_->dependency(i)); + const string& name = file_->dependency(i)->name(); + bool public_import = (public_import_names.count(name) != 0); + generator->SetIsPublicDependency(public_import); + dependency_generators_.push_back(generator); + } + } + return dependency_generators_; +} + +} // namespace objectivec +} // namespace compiler +} // namespace protobuf +} // namespace google |