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_message.h>
35#include <google/protobuf/compiler/code_generator.h>
36#include <google/protobuf/io/printer.h>
37#include <google/protobuf/io/zero_copy_stream_impl.h>
38#include <google/protobuf/stubs/stl_util.h>
39#include <google/protobuf/stubs/strutil.h>
40#include <sstream>
41
42namespace google {
43namespace protobuf {
44
45// This is also found in GPBBootstrap.h, and needs to be kept in sync.  It
46// is the version check done to ensure generated code works with the current
47// runtime being used.
48const int32 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30001;
49
50namespace compiler {
51namespace objectivec {
52
53FileGenerator::FileGenerator(const FileDescriptor *file, const Options& options)
54    : file_(file),
55      root_class_name_(FileClassName(file)),
56      is_public_dep_(false),
57      options_(options) {
58  for (int i = 0; i < file_->enum_type_count(); i++) {
59    EnumGenerator *generator = new EnumGenerator(file_->enum_type(i));
60    enum_generators_.push_back(generator);
61  }
62  for (int i = 0; i < file_->message_type_count(); i++) {
63    MessageGenerator *generator =
64        new MessageGenerator(root_class_name_, file_->message_type(i), options_);
65    message_generators_.push_back(generator);
66  }
67  for (int i = 0; i < file_->extension_count(); i++) {
68    ExtensionGenerator *generator =
69        new ExtensionGenerator(root_class_name_, file_->extension(i));
70    extension_generators_.push_back(generator);
71  }
72}
73
74FileGenerator::~FileGenerator() {
75  STLDeleteContainerPointers(dependency_generators_.begin(),
76                             dependency_generators_.end());
77  STLDeleteContainerPointers(enum_generators_.begin(), enum_generators_.end());
78  STLDeleteContainerPointers(message_generators_.begin(),
79                             message_generators_.end());
80  STLDeleteContainerPointers(extension_generators_.begin(),
81                             extension_generators_.end());
82}
83
84void FileGenerator::GenerateHeader(io::Printer *printer) {
85  printer->Print(
86      "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
87      "// source: $filename$\n"
88      "\n",
89      "filename", file_->name());
90
91  printer->Print(
92      "#import \"GPBProtocolBuffers.h\"\n"
93      "\n");
94
95  // Add some verification that the generated code matches the source the
96  // code is being compiled with.
97  printer->Print(
98      "#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != $protoc_gen_objc_version$\n"
99      "#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.\n"
100      "#endif\n"
101      "\n",
102      "protoc_gen_objc_version",
103      SimpleItoa(GOOGLE_PROTOBUF_OBJC_GEN_VERSION));
104
105  const vector<FileGenerator *> &dependency_generators = DependencyGenerators();
106  for (vector<FileGenerator *>::const_iterator iter =
107           dependency_generators.begin();
108       iter != dependency_generators.end(); ++iter) {
109    if ((*iter)->IsPublicDependency()) {
110      printer->Print("#import \"$header$.pbobjc.h\"\n",
111                     "header", (*iter)->Path());
112    }
113  }
114
115  printer->Print(
116      "// @@protoc_insertion_point(imports)\n"
117      "\n"
118      "#pragma clang diagnostic push\n"
119      "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"
120      "\n"
121      "CF_EXTERN_C_BEGIN\n"
122      "\n");
123
124  set<string> fwd_decls;
125  for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
126       iter != message_generators_.end(); ++iter) {
127    (*iter)->DetermineForwardDeclarations(&fwd_decls);
128  }
129  for (set<string>::const_iterator i(fwd_decls.begin());
130       i != fwd_decls.end(); ++i) {
131    printer->Print("$value$;\n", "value", *i);
132  }
133  if (fwd_decls.begin() != fwd_decls.end()) {
134    printer->Print("\n");
135  }
136
137  printer->Print(
138      "NS_ASSUME_NONNULL_BEGIN\n"
139      "\n");
140
141  // need to write out all enums first
142  for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin();
143       iter != enum_generators_.end(); ++iter) {
144    (*iter)->GenerateHeader(printer);
145  }
146
147  for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
148       iter != message_generators_.end(); ++iter) {
149    (*iter)->GenerateEnumHeader(printer);
150  }
151
152  // For extensions to chain together, the Root gets created even if there
153  // are no extensions.
154  printer->Print(
155      "#pragma mark - $root_class_name$\n"
156      "\n"
157      "/// Exposes the extension registry for this file.\n"
158      "///\n"
159      "/// The base class provides:\n"
160      "/// @code\n"
161      "///   + (GPBExtensionRegistry *)extensionRegistry;\n"
162      "/// @endcode\n"
163      "/// which is a @c GPBExtensionRegistry that includes all the extensions defined by\n"
164      "/// this file and all files that it depends on.\n"
165      "@interface $root_class_name$ : GPBRootObject\n"
166      "@end\n"
167      "\n",
168      "root_class_name", root_class_name_);
169
170  if (extension_generators_.size() > 0) {
171    // The dynamic methods block is only needed if there are extensions.
172    printer->Print(
173        "@interface $root_class_name$ (DynamicMethods)\n",
174        "root_class_name", root_class_name_);
175
176    for (vector<ExtensionGenerator *>::iterator iter =
177             extension_generators_.begin();
178         iter != extension_generators_.end(); ++iter) {
179      (*iter)->GenerateMembersHeader(printer);
180    }
181
182    printer->Print("@end\n\n");
183  }  // extension_generators_.size() > 0
184
185  for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
186       iter != message_generators_.end(); ++iter) {
187    (*iter)->GenerateMessageHeader(printer);
188  }
189
190  printer->Print(
191      "NS_ASSUME_NONNULL_END\n"
192      "\n"
193      "CF_EXTERN_C_END\n"
194      "\n"
195      "#pragma clang diagnostic pop\n"
196      "\n"
197      "// @@protoc_insertion_point(global_scope)\n");
198}
199
200void FileGenerator::GenerateSource(io::Printer *printer) {
201  printer->Print(
202      "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
203      "// source: $filename$\n"
204      "\n",
205      "filename", file_->name());
206
207  string header_file = Path() + ".pbobjc.h";
208  printer->Print(
209      "#import \"GPBProtocolBuffers_RuntimeSupport.h\"\n"
210      "#import \"$header_file$\"\n",
211      "header_file", header_file);
212  const vector<FileGenerator *> &dependency_generators =
213      DependencyGenerators();
214  for (vector<FileGenerator *>::const_iterator iter =
215           dependency_generators.begin();
216       iter != dependency_generators.end(); ++iter) {
217    if (!(*iter)->IsPublicDependency()) {
218      printer->Print("#import \"$header$.pbobjc.h\"\n",
219                     "header", (*iter)->Path());
220    }
221  }
222  printer->Print(
223      "// @@protoc_insertion_point(imports)\n"
224      "\n"
225      "#pragma clang diagnostic push\n"
226      "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n"
227      "\n");
228
229  printer->Print(
230      "#pragma mark - $root_class_name$\n"
231      "\n"
232      "@implementation $root_class_name$\n\n",
233      "root_class_name", root_class_name_);
234
235  // Generate the extension initialization structures for the top level and
236  // any nested messages.
237  ostringstream extensions_stringstream;
238  if (file_->extension_count() + file_->message_type_count() > 0) {
239    io::OstreamOutputStream extensions_outputstream(&extensions_stringstream);
240    io::Printer extensions_printer(&extensions_outputstream, '$');
241    for (vector<ExtensionGenerator *>::iterator iter =
242             extension_generators_.begin();
243         iter != extension_generators_.end(); ++iter) {
244      (*iter)->GenerateStaticVariablesInitialization(&extensions_printer);
245    }
246    for (vector<MessageGenerator *>::iterator iter =
247             message_generators_.begin();
248         iter != message_generators_.end(); ++iter) {
249      (*iter)->GenerateStaticVariablesInitialization(&extensions_printer);
250    }
251    extensions_stringstream.flush();
252  }
253
254  // If there were any extensions or this file has any dependencies, output
255  // a registry to override to create the file specific registry.
256  const string& extensions_str = extensions_stringstream.str();
257  if (extensions_str.length() > 0 || file_->dependency_count() > 0) {
258    printer->Print(
259        "+ (GPBExtensionRegistry*)extensionRegistry {\n"
260        "  // This is called by +initialize so there is no need to worry\n"
261        "  // about thread safety and initialization of registry.\n"
262        "  static GPBExtensionRegistry* registry = nil;\n"
263        "  if (!registry) {\n"
264        "    GPBDebugCheckRuntimeVersion();\n"
265        "    registry = [[GPBExtensionRegistry alloc] init];\n");
266
267    printer->Indent();
268    printer->Indent();
269
270    if (extensions_str.length() > 0) {
271      printer->Print(
272          "static GPBExtensionDescription descriptions[] = {\n");
273      printer->Indent();
274      printer->Print(extensions_str.c_str());
275      printer->Outdent();
276      printer->Print(
277          "};\n"
278          "for (size_t i = 0; i < sizeof(descriptions) / sizeof(descriptions[0]); ++i) {\n"
279          "  GPBExtensionDescriptor *extension =\n"
280          "      [[GPBExtensionDescriptor alloc] initWithExtensionDescription:&descriptions[i]];\n"
281          "  [registry addExtension:extension];\n"
282          "  [self globallyRegisterExtension:extension];\n"
283          "  [extension release];\n"
284          "}\n");
285    }
286
287    const vector<FileGenerator *> &dependency_generators =
288        DependencyGenerators();
289    for (vector<FileGenerator *>::const_iterator iter =
290             dependency_generators.begin();
291         iter != dependency_generators.end(); ++iter) {
292      printer->Print(
293          "[registry addExtensions:[$dependency$ extensionRegistry]];\n",
294          "dependency", (*iter)->RootClassName());
295    }
296
297    printer->Outdent();
298    printer->Outdent();
299
300    printer->Print(
301        "  }\n"
302        "  return registry;\n"
303        "}\n"
304        "\n");
305  }
306
307  printer->Print("@end\n\n");
308
309  // File descriptor only needed if there are messages to use it.
310  if (message_generators_.size() > 0) {
311    string syntax;
312    switch (file_->syntax()) {
313      case FileDescriptor::SYNTAX_UNKNOWN:
314        syntax = "GPBFileSyntaxUnknown";
315        break;
316      case FileDescriptor::SYNTAX_PROTO2:
317        syntax = "GPBFileSyntaxProto2";
318        break;
319      case FileDescriptor::SYNTAX_PROTO3:
320        syntax = "GPBFileSyntaxProto3";
321        break;
322    }
323    printer->Print(
324        "#pragma mark - $root_class_name$_FileDescriptor\n"
325        "\n"
326        "static GPBFileDescriptor *$root_class_name$_FileDescriptor(void) {\n"
327        "  // This is called by +initialize so there is no need to worry\n"
328        "  // about thread safety of the singleton.\n"
329        "  static GPBFileDescriptor *descriptor = NULL;\n"
330        "  if (!descriptor) {\n"
331        "    GPBDebugCheckRuntimeVersion();\n"
332        "    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@\"$package$\"\n"
333        "                                                     syntax:$syntax$];\n"
334        "  }\n"
335        "  return descriptor;\n"
336        "}\n"
337        "\n",
338        "root_class_name", root_class_name_,
339        "package", file_->package(),
340        "syntax", syntax);
341  }
342
343  for (vector<EnumGenerator *>::iterator iter = enum_generators_.begin();
344       iter != enum_generators_.end(); ++iter) {
345    (*iter)->GenerateSource(printer);
346  }
347  for (vector<MessageGenerator *>::iterator iter = message_generators_.begin();
348       iter != message_generators_.end(); ++iter) {
349    (*iter)->GenerateSource(printer);
350  }
351
352  printer->Print(
353    "\n"
354    "#pragma clang diagnostic pop\n"
355    "\n"
356    "// @@protoc_insertion_point(global_scope)\n");
357}
358
359const string FileGenerator::Path() const { return FilePath(file_); }
360
361const vector<FileGenerator *> &FileGenerator::DependencyGenerators() {
362  if (file_->dependency_count() != dependency_generators_.size()) {
363    set<string> public_import_names;
364    for (int i = 0; i < file_->public_dependency_count(); i++) {
365      public_import_names.insert(file_->public_dependency(i)->name());
366    }
367    for (int i = 0; i < file_->dependency_count(); i++) {
368      FileGenerator *generator =
369          new FileGenerator(file_->dependency(i), options_);
370      const string& name = file_->dependency(i)->name();
371      bool public_import = (public_import_names.count(name) != 0);
372      generator->SetIsPublicDependency(public_import);
373      dependency_generators_.push_back(generator);
374    }
375  }
376  return dependency_generators_;
377}
378
379}  // namespace objectivec
380}  // namespace compiler
381}  // namespace protobuf
382}  // namespace google
383